• poj 2754 Similarity of necklaces 2 转换成多重背包,单调队列优化/ 二进制优化


      贴个官方解题报告

    Similarity of necklaces 2
    
    这个问题是一个012背包问题。我们知道01背包只要逆向线性检索,无限背包只要正向检索。012背包就是说,每个物品有一个数量上限。这个问题可以用log方法,也存在线性方法,需要维护一个递增/递减序列。
    
    我们先把所有的Table都放成下限,接下来我们可以算出它距离总和为0还需要增加多少。对于1<=i<=M,它可以看成这样一个物品:体积为Multi[i],费用为Pairs[i],数量为Up[i]-Low[i]。然后就得到一个012背包问题了。
    
    复杂度约为:O(M*背包大小)。其中背包大小不超过M*20*25=200*20*25=100000

      单调队列优化解法:

        多重背包状态方程:

            

        令  转换下得到:

            

        因为 , 当 j 确定时, 则x的取值范围为,  

        也就是说    ,  也可看作为 

        则可得

                ,其中 j <= k

        那么意味着,对于确定的 j, 求 Xk 时候,我们只需要对  维护一个单调队列即可。

        对于任意的 Xi < Xj, 若 Xb 优于 Xa,则必将满足如下要求, 

        

        这种写法 AC时间是 1500ms,有点慢    

    View Code
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    
    const int maxn = 210;
    const int N = 100000+10;
    const int inf = -2139062144;
    int dp[2][N];
    int P[maxn], M[maxn], Low[maxn], Up[maxn];
    int n, m, Q[N], cnt[maxn], val;
    int max(int a,int b) {return a>b?a:b;}
    int main()
    { 
        while( scanf("%d", &n) != EOF)
        {
            m = 0; val = 0;    
            for(int i = 1; i <= n; i++)
            {    
                scanf("%d%d%d%d",P+i,M+i,Low+i,Up+i);
                m += Low[i]*M[i];
                val += Low[i]*P[i];    
                cnt[i] = Up[i]-Low[i];    
            }    
            memset(dp, 0x80, sizeof(dp));
            dp[0][0] = 0;    
        
            m = -m;
            int cur = 0;
            for(int i = 1; i <= n; i++)
            {
                int nxt = cur^1;
                // 枚举剩余系j    
                for(int j = 0; j < M[i]; j++)
                {
                    //单调队列队首指针qh,队尾指针qe, 当前最大长度len    
                    int qh = 0, qe = -1, len = cnt[i]*M[i];
                    for(int k = j; k <= m; k += M[i] )
                    {
                        if( dp[cur][k] != -inf )
                        {
                            while( (qh <= qe) && (dp[cur][ Q[qe] ]+(k-Q[qe])*P[i]/M[i] <= dp[cur][k] ) )
                                qe--;
                            Q[++qe] = k;    
                            while( (qh <= qe) && (k-Q[qh]>len) ) qh++;
                            if(qh <= qe)    dp[nxt][k] = dp[cur][ Q[qh] ] + (k-Q[qh])*P[i]/M[i];
                            else    dp[nxt][k] = -inf;    
                        }
                    }
                }
                cur = nxt;    
            }
            printf("%d\n", dp[cur][m]+val );    
        }
        return 0;
    }

        

        将其转换成 01背包:

          套用 背包九讲 的二进制写法就可以了,一样将Table取下限,转换出一个物品数量。

          模板是处理第i种物品时, 若其 体积*数量 >= 总背包大小, 则当作完全背包处理,

          否则 将其按照 二进制表示的形式,当作一次 01背包来处理。代码400MS左右,比上面写法快

    View Code
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    using namespace std;
    
    const int inf = 0x80808080;
    const int N = 100010;
    int n, V, val;
    int P[210], M[210], cnt[210];
    int dp[N];
    
    int max(int a,int b)
    { return a>b?a:b; }
    
    void ZeroOnePack( int cost, int weight )
    {
        for(int v = V; v >= cost; v-- )    
            dp[v] = max( dp[v], dp[v-cost] + weight );
    }
    void CompletePack( int cost, int weight )
    {
        for(int v = cost; v <= V; v++ )
            dp[v] = max( dp[v], dp[v-cost] + weight );
    }
    void MultiplePack( int cost, int weight, int num )
    {
        if( cost*num >= V )
        {    
            CompletePack( cost, weight );
            return ;
        }
        int k = 1;
        while( k <= num )
        {
            ZeroOnePack( k*cost, k*weight );
            num = num-k;
            k <<= 1;
        }
        ZeroOnePack( num*cost, num*weight );
    }
    int main()
    {
        while( scanf("%d",&n) != EOF)
        {
            V = 0; val = 0;
            int up, low;
            for(int i = 1; i <= n; i++)
            {
                scanf("%d%d%d%d",P+i,M+i,&low,&up);
                V += low*M[i];
                val += low*P[i];
                cnt[i] = up-low;    
            }
            V = -V;
            memset( dp, 0x80, sizeof(dp));
            dp[0] = 0;
            for(int i = 1; i <= n; i++)
                MultiplePack( M[i], P[i], cnt[i] );    
            
            printf("%d\n", dp[V]+val );
        }    
        return 0;
    }

        

        

  • 相关阅读:
    游戏开发之路小结(二):关于第一人称射击游戏开发实战小结
    游戏开发之路小结(一):关于太空射击游戏开发实战小结
    游戏开发要涉及的几个方面
    短短几行代码实现让摄像机跟随着物体效果
    关于移动设备几种屏幕输入方式的小结
    软件的体系架构摘要
    jQuery入门笔记之(一)选择器引擎-【转】
    ASCII码对照表
    将数字转出大写如:100转换后结果为一佰
    google搜索技巧总结
  • 原文地址:https://www.cnblogs.com/yefeng1627/p/2866808.html
Copyright © 2020-2023  润新知