poj 2754/1014 多重背包之二进制优化

来源:互联网 发布:淘宝内容营销都有哪些 编辑:程序博客网 时间:2024/06/06 19:42

2754题意:给定M(1<=M<=200)以及长度为M的四个数组,分别记为Pairs、Multi、Low、Up,你需要构造一个长度为M的数组Table(其中Low[i]<=Table[i]<=Up[i]),满足∑Multi[i]*Table[i] = 0,且使得∑Pairs[i]*Table[i]尽量大。输入满足至少有一个解。

思路:如果直接上dp,那么复杂度最大可达M*极差*50。其中M是序列长度,50是因为每个数最多有50个取值的可能,极差是∑Multi[i]*Table[i]能达到的最大值和最小值之间的差值。无法承受。下面考虑将其转化为多重背包问题。

将下界单独拿出来作为一部分进行计算,因此[Low[i],Up[i]]就转化为[0, U[i]-L[i]]的一个多重背包。M[i]和P[i]均进行单独的下界计算。计算出 T = L[1]*M[1]+L[2]*M[2].... 之后, 就是一个关于容量T刚好放满的多重背包。

多重背包有一个二进制优化,参见(http://www.cnblogs.com/favourmeng/archive/2012/09/07/2675580.html)。

#include <cstdio>#include <algorithm>#include <cstdlib>#define N 205#define ORI 50000#define INF 0x7fffffffusing namespace std;int dp[200005];int n,m;int up[N],low[N],p[N],w[N];int main(){    while(scanf("%d",&n) != EOF){        m = 0;        int presum = 0;        for(int i = 0;i<n;i++){            scanf("%d %d %d %d",&p[i],&w[i],&low[i],&up[i]);            m += w[i]*low[i];            presum += p[i]*low[i];            up[i] -= low[i];        }        m = -m;        for(int i = 1;i<=m;i++)            dp[i] = -INF;        dp[0] = 0;        for(int i = 0;i<n;i++){            int num = 1;            while(up[i] > num){                for(int j = m;j>=num*w[i];j--)                    dp[j] = max(dp[j], dp[j-num*w[i]]+num*p[i]);                up[i] -= num;                num <<= 1;            }            for(int j = m;j>=up[i]*w[i];j--)                dp[j] = max(dp[j], dp[j-w[i]*up[i]]+p[i]*up[i]);        }        printf("%d\n",dp[m]+presum);            }    return 0;}

1014题意:有六种石头,分别价值1~6,现在给出价值分别为1~6的石头的数量(石头总数不超过20000)。问能否把这些石头分成两类,使得每类的价值总和相等。

思路:首先求价值总和,如果是奇数必然不能。否则就变成了一个多重背包问题,看看能否凑成总和的一半,用二进制优化。

#include <cstdio>#include <cstring>#include <string>#include <cstdlib>#include <vector>#include <queue>#include <algorithm>#include <cmath>#include <map>#include <set>#include <iostream>#define N 120005#define INF 0x3fffffffint s[7],c = 1;bool dp[N>>1];using namespace std;int main(){    while(1){        int m = 0;        for(int i = 1;i<=6;i++){            scanf("%d",&s[i]);            m += i*s[i];        }        if(m == 0)            break;        if(m & 1)            printf("Collection #%d:\nCan't be divided.\n\n",c++);        else{            memset(dp,false,sizeof(dp));            dp[0] = true;            m >>= 1;            for(int i = 1;i<=6;i++){                if(!s[i])                    continue;                int num = 1;                while(s[i] > num){                    for(int j = m;j-num*i>=0;--j)                        dp[j] |= dp[j - num*i];                    s[i] -= num;                    num <<= 1;                }                for(int j = m; j - s[i]*i>=0; --j)                    dp[j] |= dp[j - s[i]*i];            }            if(dp[m])                printf("Collection #%d:\nCan be divided.\n\n",c++);            else                printf("Collection #%d:\nCan't be divided.\n\n",c++);        }    }    return 0;}


0 0
原创粉丝点击