01背包问题。
pku 3624 Charm Bracelet
http://poj.org/problem?id=3624
最裸的01背包。
#include <cstdio> #include <cstring> #include <iostream> #define CL(a,num) memset((a),(num),(sizeof(a))) #define N 3405 #define M 12888 using namespace std; int c[N],w[N]; int f[M]; int main(){ int i,j; int n,m; scanf("%d%d",&n,&m); for (i = 0; i < n; ++i){ scanf("%d%d",&c[i],&w[i]); } CL(f,0); for (i = 0; i < n; ++i){ for (j = m; j >= c[i]; --j){ f[j] = max(f[j],f[j - c[i]] + w[i]); } } printf("%d\n",f[m]); return 0; }
pku 3628 Bookshelf 2
http://poj.org/problem?id=3628
题意:
有n头牛,他们都有各自的身高,有一个书架的高度为m,求这些牛能够累加起来的大于等于m的最小高度。
思路:
这道题类似于楼爷的O(V*n)解多重背包时的思路,只不过这里不用记录每个牛使用了多少次因为每个牛就一个。因为要保证每个仅使用一次所以V要逆序。就转化成了01背包的形式。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a , b) ((a) < (b) ? (a) : (b)) #define Max(a , b) ((a) > (b) ? (a) : (b)) #define ll __int64 #define inf 0x7f7f7f7f #define MOD 100000007 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 100007 #define M 100007 #define N 20000007 using namespace std; //freopen("din.txt","r",stdin); bool f[N]; int w[N]; int main(){ int i,j; int n,m; while (~scanf("%d%d",&n,&m)){ int V = 0; for (i = 0; i < n; ++i){ scanf("%d",&w[i]); V += w[i]; } for (i = 0; i <= V; ++i) f[i] = 0; f[0] = 1;//0肯定要标记成出现过 for (i = 0; i < n; ++i){ for (j = V; j >= w[i]; --j){//这里保f[j - w[i]]里面没有出现过i的累加情况 if (!f[j] && f[j - w[i]]){ f[j] = 1; } } } for (i = m; i <= V; ++i){ if (f[i]) break; } printf("%d\n",i - m); } return 0; }
暴力dfs选与不选可过。
pku 3211 Washing Clothes
http://poj.org/problem?id=3211
题意:
Dearboy 和他的女朋友一起洗衣服,这些衣服分为n种颜色,总共有m件,每件衣服对应了一个洗刷需要的时间,他和他的女朋友可以同时洗,为了避免混色只有洗完了这种颜色的所有衣服后才能洗刷下一种颜色的衣服。问洗完所有衣服所需要的时间。
思路:
首先将每种衣服按颜色分类,然后求出每种颜色衣服洗刷所需的时间和sum[i]我们求出能够尽量组合出来的小于等于sum[i]/2的最大值,然后求sum[i] - x即可。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a , b) ((a) < (b) ? (a) : (b)) #define Max(a , b) ((a) > (b) ? (a) : (b)) #define ll __int64 #define inf 0x7f7f7f7f #define MOD 100000007 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 100007 #define M 100007 #define N 100007 using namespace std; //freopen("din.txt","r",stdin); bool f[N]; char str[12][12]; int a[12][107]; int sum[12],p[12]; int n,m; int getnum(char *s){ for (int i = 0; i < n; ++i){ if (!strcmp(s,str[i])){ return i; } } } int main(){ int i,j,k; int val; char tpstr[12]; while (~scanf("%d%d",&n,&m)){ if (!n && !m) break; CL(p,0); CL(sum,0); for (i = 0; i < n; ++i) scanf("%s",str[i]); for (i = 0; i < m; ++i){ scanf("%d%s",&val,tpstr); int num = getnum(tpstr); a[num][p[num]++] = val; sum[num] += val; } int ans = 0; for (i = 0; i < n; ++i){ for (j = 0; j <= sum[i]; ++j) f[j] = 0; f[0] = 1; for (j = 0; j < p[i]; ++j){ for (k = sum[i]/2 - a[i][j]; k >= 0; --k){ if (f[k]) f[k + a[i][j]] = 1; } } for (j = sum[i]/2; j >= 0; --j) if (f[j]) break; ans += (sum[i] - j); } printf("%d\n",ans); } return 0; }
pku 1745 Divisibility
http://poj.org/problem?id=1745
题意:
给你n个数,这n个数的值的取值为<|1000|,添加n-1个加号或者-问得到的值中是否存在能够整除K的值。
思路:
其实也不是严格的01背包了,只是思想类似,首先是同余的应用(a +b)%m = (a%m + b%m)%m
由于m<=100所以就将数压缩到0-100了,dp[i][j]表示前i个数是否可以形成j这个数。我们只要往背包里塞,看是否能够得到0这个值就好了。注意这里负数区域的处理
-7%3 = 2
#include <cstdio> #include <cstring> #include <iostream> #define CL(a,num) memset((a),(num),(sizeof(a))) #define N 10007 #define M 107 using namespace std; int dp[N][M]; int a[N]; int n,m; //这里来处理负数取余 int MOD(int val,int mod){ if (val%mod == 0) return 0; else{ if (val > 0) return val%mod; else{ val = -val; return (mod*(val/mod + 1) - val); } } } int main(){ int i,j; scanf("%d%d",&n,&m); for (i = 1; i <= n; ++i){ scanf("%d",&a[i]); a[i] = MOD(a[i],m); } CL(dp,0); dp[0][0] = 1; int tmp; for (i = 1; i <= n; ++i){ for (j = 0; j < m; ++j){ if (dp[i - 1][j]){ tmp = MOD(j + a[i],m); dp[i][tmp] = 1; tmp = MOD(j - a[i],m); dp[i][tmp] = 1; } } } if (dp[n][0]) puts("Divisible"); else puts("Not divisible"); return 0; }
pku 1976 A Mini Locomotive 01背包装+连续线段长度
http://poj.org/problem?id=1976
题意:
有三个火车头,n个车厢,每个车厢里面对应的有一定的人数。规定每个火车头最多拉m个连续的车厢而且他们拉的车厢一定是从左到右连续的,问它能够拉的最多的人数;
思路:
类似01背包的解法,首先每个火车最多拉m个连续的车厢,这里我们把只要存在连续的m个车厢的就看成一个物品。相当于往背包容量为3的背包里面放物品所得的最大价值量。但是这里注意每连续的m个车厢为一个物品,f[i][j] = max(f[i - 1][j],f[i - m][j - 1] + sum[i] - sum[i - m]); 这里对于每个物品要么不放,要么就是放(放连续的m个车厢)
sum[i] = a[0] + a[1] + ... + a[i];
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a , b) ((a) < (b) ? (a) : (b)) #define Max(a , b) ((a) > (b) ? (a) : (b)) #define ll __int64 #define inf 0x7f7f7f7f #define MOD 100000007 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 100007 #define M 207 #define N 50007 using namespace std; //freopen("din.txt","r",stdin); int f[N][4]; int a[N],sum[N]; int main(){ //freopen("din.txt","r",stdin); int T,i,j; int n,m; scanf("%d",&T); while (T--){ scanf("%d",&n); sum[0] = 0; for (i = 1; i <= n; ++i){ scanf("%d",&a[i]); sum[i] = sum[i - 1] + a[i]; } scanf("%d",&m); CL(f,0); for (i = 1; i <= n; ++i){ for (j = 1; j <= 3; ++j){ int tp = i - m; if (tp < 0) tp = 0;//因为最多是m个车厢,当不足是我们可以选择拉小于m个的车厢 f[i][j] = max(f[i - 1][j],f[tp][j - 1] + sum[i] - sum[tp]); } } printf("%d\n",f[n][3]); } return 0; }
pku 2923 Relocation 状态压缩+01背包装状态
http://poj.org/problem?id=2923
题意:
用两辆车搬家,每辆车都有各自的载重量c1,c2如果超过他的载重量汽车就完蛋。有n个家具每个家具都有各自的重量,这里保证每个家具肯定能够用其中一辆车运走,即wi < max(c1,c2);
问最少需要多少次搬运将所有家具运完。
思路:
前边起初做的背包是往背包里面放物品,后来是放长度为m的连续的线段,而这里又成了放某种状态,感叹背包伟大啊。。。。
首先将所有状态中的能够一次用c1,c2运走的状态都选出来,选的过程还是利用01背包来组合出最大的小于等于c1的运输量,然后判断c2是否可以。然后利用01背包将选出的状态往背包容量为最终状态的MSK里面放记录最小次数即可。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a , b) ((a) < (b) ? (a) : (b)) #define Max(a , b) ((a) > (b) ? (a) : (b)) #define ll __int64 #define inf 0x7f7f7f7f #define MOD 100000007 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 100007 #define M 1027 #define N 15 using namespace std; //freopen("din.txt","r",stdin); int f[M]; int msk[M]; int a[N],n; int c1,c2; bool isok(int x){ int sum = 0,i,j; for (i = 0; i <= c1; ++i) f[i] = 0; f[0] = 1; for (i = 0; i < n; ++i){ if (x&(1<<i)){ sum += a[i]; for (j = c1; j >= a[i]; --j){ if (!f[j] && f[j - a[i]]) f[j] = 1; } } } for (i = c1; i >= 0; --i) if (f[i]) break; if (sum - i <= c2) return 1; else return 0; } int main(){ //freopen("din.txt","r",stdin); int i,j,T,cas = 1; scanf("%d",&T); while (T--){ scanf("%d%d%d",&n,&c1,&c2); if (c1 > c2) swap(c1,c2);//c1始终是最小这样在isok里面就优化了。 for (i = 0; i < n; ++i) scanf("%d",&a[i]); int MSK = (1<<n); int len = 0; for (i = 1; i < MSK; ++i){ if (isok(i)) msk[len++] = i;//记录满足要求的状态 } for (i = 0; i < M; ++i) f[i] = inf; f[0] = 0; //01背包记录最小搬运次数 for (i = 0; i < len; ++i){ for (j = MSK - 1; j >= msk[i]; --j){ if (((j&msk[i]) == msk[i]) && f[j - msk[i]] != inf) f[j] = min(f[j],f[j - msk[i]] + 1); } } printf("Scenario #%d:\n",cas++); printf("%d\n\n",f[MSK - 1]); } return 0; }
pku 1837 Balance 往背包里面放值
http://poj.org/problem?id=1837
题意:
一个天枰,左右两个臂膀的长度为15,有n个挂砝码的位置,和m个重量不同的砝码,砝码的最大重量为25,问将所有砝码都挂上得到平衡的可能数。
思路:
这想当于往背包里放值求最终值为0的种数。这里可以算出分别挂到两边的极限值为-7500 到7500 我们可以经他们映射到0-15000 然后往背包里面放值,看最后总出现0也即7500的个数。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a , b) ((a) < (b) ? (a) : (b)) #define Max(a , b) ((a) > (b) ? (a) : (b)) #define ll __int64 #define inf 0x7f7f7f7f #define MOD 100000007 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 100007 #define M 15007 #define N 25 using namespace std; //freopen("din.txt","r",stdin); int dp[N][M]; int pos[N],g[N]; const int L = 7500; int main(){ //freopen("din.txt","r",stdin); int i,j,k; int n,m; scanf("%d%d",&n,&m); for (i = 1; i <= n; ++i) scanf("%d",&pos[i]); for (i = 1; i <= m; ++i) scanf("%d",&g[i]); CL(dp,0); for (i = 1; i <= n; ++i){ dp[1][g[1]*pos[i] + L]++; } for (i = 2; i <= m; ++i){ for (j = 0; j <= 15000; ++j){ if (dp[i - 1][j]){ for (k = 1; k <= n; ++k){ dp[i][j + pos[k]*g[i]] += dp[i - 1][j]; } } } } printf("%d\n",dp[m][7500]); return 0; }
更新中。。。