首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

多重双肩包-zoj_2156

2012-10-11 
多重背包--zoj_2156???????? 首先介绍经典的三种背包问题,0-1背包,完全背包,多重背包。0-1背包是背包问题的

多重背包--zoj_2156

???????? 首先介绍经典的三种背包问题,0-1背包,完全背包,多重背包。0-1背包是背包问题的最简形式,其他的很多背包问题都可以转化为0-1背包来解决,而0-1背包问题也有非常简单的解法,时间效率为O(V*N),空间消耗为O(V),这里不要小看空间消耗,在数据量很大时,大量的取地址操作也会占用不少的时间消耗。

??????? 0-1背包问题可以这样描述:给出N个背包,每个背包的重量为c[i],价值为v[i],在N个背包中选取k个,每个最多选1次,使得总的重量小于V而,总的价值最大。

??????? 用DP,以dp[i][j]表述更新第i个背包,总重量为j时的最大价值。显然可以有以下状态转移方程dp[i][j]=max(dp[i-1][j],dp[i-1][j-c[i]]+v[i]);简单分析一下效率发现是O(V*N),空间消耗为O(V*N).

?????? 那么如何简化空间消耗呢,运用了很巧妙的一个技巧,在遍历j时从大到小遍历即可用dp[j]表示新第i个背包,总重量为j时的最大价值。仔细想想WSM会这样呢?如果从小到大遍历

dp[j]是由dp[j]和dp[j-c[i]]+v[i]更新而来的,而此时的dp[j]已经是dp[i][j]了,但我们需要的是dp[i-1][j]。仔细想想就可以明白。

??????? 完全背包问题可以这样描述:给出N个背包,每个背包的重量为c[i],价值为v[i],在N个背包中选取k个,每个次数不限,使得总的重量小于V而,总的价值最大。

?????? 朴素的解法就是在0-1背包的基础上遍历k,分析一下效率为V*sum(V/c[i])

?????? 也有拆分法效率为V*sum(log(V/c[i])),具体见背包9讲

?????? 这里要重点介绍O(V*N)法,很简单也很巧妙,就是0-1背包优化解法的正向更新,在遍历j时从小到大遍历即可用dp[j]表示新第i个背包,总重量为j时的最大价值。

?????? WSM会这样呢?仔细想想也很简单,在0-1背包的时候,我们希望更新dp[j]的时候dp[j]和dp[j-c[i]]+v[i]都是在i-1时更新的值,而在完全背包时,每个背包可以取无限次,因此我们在更新dp[j]的时候恰恰需要的是在i时的dp[j]和dp[j-c[i]]+v[i]。

?????? 最后介绍多重背包:给出N个背包,每个背包的重量为c[i],价值为v[i],在N个背包中选取k个,每个次数为n[i],使得总的重量小于V而,总的价值最大。

?????? 显然也可以遍历k,效率是V*sum(n[i]),这里介绍一种拆分法V*sum(log(n[i])),

很简单,把每一个系数为n[i]的背包拆分为,系数为2^0,2^1,2^2...n[i]-2^k的背包,背包的重量和价值都乘上相对应的系数即可。拆分后用0-1背包解。

zoj_2156

#include<cstdio>#include<algorithm>using namespace std;pair<int,int> clist[200];int p,c1,c2,c3,c4;int total,mid;int inf=-999999;//int dp[200][10005];int dp[10005];//int path[200][10005];int path[10005];int cnt[10005];int flag[200];void init(){total=0;int tmp=1;mid=c1;while(mid>tmp){mid=mid-tmp;clist[++total]=make_pair(1*tmp,1*tmp);flag[total]=1;tmp*=2;}clist[++total]=make_pair(1*mid,1*mid);flag[total]=1;mid=c2;tmp=1;while(mid>tmp){mid=mid-tmp;clist[++total]=make_pair(5*tmp,1*tmp);flag[total]=2;tmp*=2;}clist[++total]=make_pair(5*mid,1*mid);flag[total]=2;mid=c3;tmp=1;while(mid>tmp){mid=mid-tmp;clist[++total]=make_pair(10*tmp,1*tmp);flag[total]=3;tmp*=2;}clist[++total]=make_pair(10*mid,1*mid);flag[total]=3;mid=c4;tmp=1;while(mid>tmp){mid=mid-tmp;clist[++total]=make_pair(25*tmp,1*tmp);flag[total]=4;tmp*=2;}clist[++total]=make_pair(25*mid,1*mid);flag[total]=4;return;}void solve(){/*for(int i=0;i<=total;++i)for(int j=0;j<=p;++j){dp[i][j]=inf;}*/for(int j=0;j<=p;++j){dp[j]=-1;cnt[j]=0;}dp[0]=0;/*for(int i=1;i<=total;++i){for(int j=0;j<=p;++j){if(j-clist[i].first>=0){if(dp[i][j]<dp[i-1][j-clist[i].first]+clist[i].second&&dp[i-1][j-clist[i].first]+clist[i].second>=0){path[i][j]=flag[i];dp[i][j]=dp[i-1][j-clist[i].first]+clist[i].second;}else{path[i][j]=0;}//dp[i][j]=max(dp[i][j],dp[i-1][j-clist[i].first]+clist[i].second);}if(dp[i][j]<dp[i-1][j]&&dp[i-1][j]>=0){dp[i][j]=dp[i-1][j];path[i][j]=0;}//dp[i][j]=max(dp[i][j],dp[i-1][j]);}}*/for(int i=1;i<=total;++i){for(int j=p;j>=clist[i].first;--j){if(dp[j]<dp[j-clist[i].first]+clist[i].second&&dp[j-clist[i].first]!=-1){path[j]=j-clist[i].first;cnt[j]=flag[i];dp[j]=dp[j-clist[i].first]+clist[i].second;}}}}int main(){freopen("e:\\zoj\\zoj_2156.txt","r",stdin);while(scanf("%d %d %d %d %d",&p,&c1,&c2,&c3,&c4)!=EOF){if(p==0&&c1==0&&c2==0&&c3==0&&c4==0)break;init();solve();if(dp[p]<0)puts("Charlie cannot buy coffee.");/*if(dp[total][p]<0)puts("Charlie cannot buy coffee.");*/else{//printf("%d\n",dp[total][p]);int c,n,d,q;c=n=d=q=0;int tmp=p;/*for(int i=total;i>=1;--i){if(path[i][tmp]==0)continue;else{if(path[i][tmp]==1){tmp-=clist[i].first;c+=clist[i].first/1;}if(path[i][tmp]==2){tmp-=clist[i].first;n+=clist[i].first/5;}if(path[i][tmp]==3){tmp-=clist[i].first;d+=clist[i].first/10;}if(path[i][tmp]==4){tmp-=clist[i].first;q+=clist[i].first/25;}}}*/while(tmp>0){if(cnt[tmp]==0)continue;else{if(cnt[tmp]==1){c+=(tmp-path[tmp])/1;tmp=path[tmp];}if(cnt[tmp]==2){n+=(tmp-path[tmp])/5;tmp=path[tmp];}if(cnt[tmp]==3){d+=(tmp-path[tmp])/10;tmp=path[tmp];}if(cnt[tmp]==4){q+=(tmp-path[tmp])/25;tmp=path[tmp];}}}printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",c,n,d,q);}}}

?

背包模板,zoj_2156

#include <iostream>#include <cstring>#include <cstdio>using namespace std;#define maxn 10002int num[4],v[4],ans[4];int sum;int dp[maxn];int fa[maxn];int cnt[maxn];void pack1(int c,int k) //0 1 背包{    int v;    for (v = sum;v>=c;v--)    {        if (dp[v] < dp[v-c]+k && dp[v-c]!=-1)        {            fa[v] = v - c;            cnt[v] = k;            dp[v] = dp[v-c]+k;        }    }    return;}void pack2(int c) //完全背包{    int v;    for (v = c;v<=sum;v++)    {        if (dp[v] < dp[v-c] + 1 && dp[v-c]!=-1)        {            fa[v] = v-c;            cnt[v] = 1;            dp[v] = dp[v-c] + 1;        }    }    return;}void pack3(int c,int a) //多重背包{    if (c*a>=sum)    {        pack2(c);        return;    }    int k = 1;    while (a-k>0)    {        pack1(k*c,k);        a = a - k;        k*=2;    }    pack1(a*c,a);    return;}int main(){    int i,tmp,k1;    v[0] = 1;    v[1] = 5;    v[2] = 10;    v[3] = 25;freopen("e:\\zoj\\zoj_2156.txt","r",stdin);    while (scanf("%d%d%d%d%d",&sum,&num[0],&num[1],&num[2],&num[3]))    {        if (sum==0&&num[0]==0&&num[1]==0&&num[2]==0&&num[3]==0)            break;        memset(dp,-1,sizeof(dp));        dp[0] = 0;        for (i=0;i<4;i++)            pack3(v[i],num[i]);        if (dp[sum] != -1)        {            tmp = sum;            memset(ans,0,sizeof(ans));            while (tmp!=0)            {                k1 = (tmp - fa[tmp])/cnt[tmp];                if (k1 == 25)                    ans[3] += cnt[tmp];                else if (k1 == 10)                    ans[2] += cnt[tmp];                else if (k1 == 5)                    ans[1] += cnt[tmp];                else                    ans[0] += cnt[tmp];                tmp = fa[tmp];            }            printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",ans[0],ans[1],ans[2],ans[3]);        }        else            printf("Charlie cannot buy coffee.\n");    }    return 0;}

?

?

热点排行