首先介绍经典的三种背包问题,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 10002 int 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; }
发表评论
-
图的割点tarjan---zoj_1119
2011-10-24 23:00 1231http://acm.zju.edu.cn/on ... -
BFS与双向BFS---zoj_1505
2011-10-09 17:14 1642http://acm.zju.edu.c ... -
静态treap+线段树---zoj_2112
2011-09-29 23:06 1708http://acm.zju.edu ... -
NIM博弈游戏,SG函数---zoj_3084,zoj_2083
2011-09-23 23:00 1316Nim游戏是两个人进行的游戏有如下规则: ... -
模式串匹配---KMP
2011-08-31 20:49 1277朴素的的模式串匹配算法通常要在向前移动文本指针 ... -
八数码问题(A*&&双向BFS)---zoj_1217
2011-08-30 22:13 1664题目地址:http://acm.zju.edu.cn/onli ... -
ac自动机以及它上面的DFA
2011-08-08 23:04 2479AC自动机(Aho-Corasick)主要用 ... -
二分图最大匹配(匈牙利算法)--zoj1516,1525,2223
2011-07-20 22:36 1185二分图G(E,V)将点集合V分成两个部分L,R ... -
网络最大流(EK)---zoj_1734
2011-07-19 16:34 2129网络最大流是图论中的一个典型问题,为了精确的定 ... -
trie和前缀检查---zoj_2876
2011-07-13 23:03 865trie树是一种多叉树,广泛用于字典检索。如 ... -
位图bitmap
2011-07-13 21:08 913bitmap是一种广泛使用的数据结构,主要用在 ... -
LAC和RMQ的转化---zoj_3195
2011-07-12 22:35 1072我擦。。纠结了好久啊,从SF到TLE,先总结2 ... -
LAC的tarjan(离线)算法---zoj_1141
2011-07-08 17:52 1657LCA就不解释了,这里主要再次复习一下LC ... -
笛卡尔树以及treap---zoj_2243
2011-07-07 15:52 2837zoj_2243笛卡 ... -
线段染色,矩形切割,离散化---zoj_2301,zoj_1128
2011-06-24 22:32 1363染色问题:在一维坐标轴上(最右端为M),有N ... -
线段树---zoj_1610
2011-06-22 21:06 743线段树是一种二叉树的拓展,在线段树每个节点中 ... -
快速排序(qsort)
2011-06-16 17:03 776快速排序是一种高效的非稳定排序,它的基本思路 ... -
斐波那契散列
2011-06-16 16:38 3071斐波那契散列法其实是一种特殊的乘法散列,先来看乘法 ... -
强连通分支(scc)---tarjan
2011-06-15 16:17 1283本文大量 ... -
单源最短路径SPFA---zoj3146
2011-06-09 15:25 940针对Bellman-Ford算法效率比较低 ...
相关推荐
ZOJ 1055 Oh, Those Achin Feet.bfs求最短路径.
最近在acm.zju.edu.cn上通过的题目的代码,都是比较有价值的题目
zoj网站中多个练习的c++解答,文件名即为题目序号。经本人测试可以使用,主要为动态规划方面的问题,希望给初学者提供帮助。
zoj吐血制作,希望大家喜欢
zoj在线评测系统前台和后台源代码,包括比赛用的客户端源代码
zoj_1004.cpp 求单词字母进出栈后能形成目标串的进出方案 广度优先搜索求解
zoj4041正确题解源代码,以及运行程序
这是一份ZOJ的ACM题解,包含大多数题目的AC程序,是学习算法的好东西~
浙江大学ZOJ源码题解,按照题目类型和难易分类。
acm中zoj1002的可运行C++程序
一个非常非常非常非常实用的zoj结题代码
问题:枫教授在一所大学教数学,他发现了一个函数,用于获得一个表达式的操作数的目的,函数命名op(i,e)可以描述如下:
能AC 通过的c++代码,包括zoj1002,1091,1789
zoj的代码实现,很好,而且很全面,全部实现。
ZOJ解题报告ZOJ解题报告ZOJ解题报告ZOJ解题报告
ZOJ上的一些水题,4.16浙江省程序设计竞赛的题目
ZOJ题解集合-截至2835。共1244个文件,C/C++,有重复
zoj 2536 这个不是用贪心做的
zoj上的3607Lazier Salesgirl AC代码及一些注释。贪心算法
zoj题目简单归类zoj题目简单归类zoj题目简单归类