DP~背包
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2602
题意: 有N 个骨头,你有一个体积为M 的背包。 给你每个骨头的价值va[i], 和体积vo[i]
思路:直接引网上原话(感觉很好理解)
这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。
用子问题定义状态:即dp[i][v]表示前i件物品放入一个容量为v的背包可以获得的最大价值。则其状态转移方程便是:dp[i][v]=max{ dp[i-1][v], dp[i-1][v-vo[i]]+va[i] }。可以压缩空间,dp[v]=max{dp[v], dp[v-vo[i]]+va[i]}
dp[i][v] 指 “将前i件物品放入容量为v的背包中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵扯前i-1件物品的问题。如果不放第i件物品,那么问题就转化为“前i-1件物品放入容量为v的背包中”,价值为dp[i-1][v];如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为v-vo[i]的背包中”,此时能获得的最大价值就是dp[i-1][v-vo[i]]再加上通过放入第i件物品获得的价值va[i]。
代码:
//f[i] = max(f[j] , f[j- vo[i]]+va[i]); #include<iostream> #include<cstdio> #include<cstring> using namespace std; #define max(a, b) (a>b?a:b) #define min(a, b) (a<b?a:b) int main() { int t; scanf("%d", &t); while(t--) { int i, n, m; // n代表物品总个数 m 代表背包总体积 int f[1002], va[1002], vo[1002]; //f[i]代表背包体积为 i 时可以装的东西的最大价值 scanf("%d%d", &n, &m); for(i=0; i<n; i++) //va数组存储每个物品的价值 scanf("%d", &va[i]); for(i=0; i<n; i++) scanf("%d", &vo[i]); // vo数组存储每个物品的体积 int j, temp; memset(f, 0, sizeof(f)); //初始化f数组 for(i=0; i<n; i++) for(j=m; j>=vo[i]; j--) //f[i] = max(f[j] , f[j- vo[i]]+va[i]); f[j] = max(f[j], f[j-vo[i]]+va[i]); printf("%d\n", f[m]); } return 0; }
// dp[i][j] = max(dp[i-1][j], dp[i-1][j-vo[i]]+va[i]) #include<iostream> #include<cstdio> #include<cstring> using namespace std; #define max(a, b) (a>b?a:b) #define min(a, b) (a<b?a:b) int va[1002], vo[1002], dp[1002][1002]; //二维数组太大时要开在main函数外面 int main() { int t; scanf("%d", &t); while(t--) { int n, m; scanf("%d%d", &n, &m); int i, j; for(i=1; i<=n; i++) scanf("%d", &va[i]); for(j=1; j<=n; j++) scanf("%d", &vo[j]); memset(dp, 0, sizeof(dp)); //初始化dp数组 for(i=1; i<=n; i++) //i对应第几个物品 for(j=0; j<=m; j++) //j对应体积 { dp[i][j] = dp[i-1][j]; //放第i件物品时,背包体积V小于第i件物品的体积,则没有放第i件物品其最大价值为其 //原值 if(j >= vo[i]) //背包体积足够时 (背包体积v大于第i 件物品的体积) dp[i][j] = max(dp[i-1][j], dp[i-1][j-vo[i]] + va[i]); } printf("%d\n", dp[n][m]); } return 0; }
完全背包: 若上一题中每件物品只有一件改成每件物品有任意多件。就成完全背包问题。。
思路:dp[i][j] 表示前i件物品在到体积为j的背包中的最大价值
最直接的思路就是 :
》dp[i+1][j] = max{ dp[i-1][j-k*v[i+1]] + k*v[i+1] | (k>=0) } k代表第i+1件物品取多少件。 然而这样就有三个for循环 算法复杂度很大~~
上面的公式可以简化:
》dp[i+1][j] = max{ dp[i][j-k*w[i+1]] + k*v[i+1] | (k>=0) }
= max{ dp[i][j], max{ dp[i+1][j-k*w[i+1]]+k*v[i+1] | k>=1} ........B
= max{ dp[i][j], max{ dp[i+1][j-w[i+1]-k*w[i+1] ]+k*v[i+1] | 0<=k } +v[i+1] } .......C
= max{ dp[i][j], dp[i+1][j-w[i+1]]+v[i+1] } .......D
B 当k取0时,当前的最大值就是 max{第i+1件不取, 第i+1件取(k>=1)件 }
C 第i+1件取(k>=1)件,前面已经算出第i+1件取(k-1)件时的最大值。(在计算dp[i+1][j-w[i]]中, 选择第i+1件物品的最大值都是 dp[i+1][j-k*w[i+1]]+k*v[i+1], 所以前面计算过可以少一些重复计算~~)
D 所以化简就行。。 最后的公式就是 : dp[i+1][j] = max{ dp[i][j], dp[i+1][j-w[i+1]]+v[i+1] }
问题:n个无区别物品,划分成m组,划分方法对mod取余 dp[i][j] 表示j划分为i组 n划分为m组,每组为ai,如果所有ai>0,则所有的ai都 -1,代表n-m的m划分,此外,如果存在ai=0,则对应了n的m-1次划分。
//dp[i][j] = dp[i-1][j]+dp[i][j-i]; int n, m, mod; void solve() { dp[0][0]=1; for(int i=1; i<=m; i++) for(int j=0; j<=n; j++) { if(j>=i) dp[i][j]=dp[i-1][j]+dp[i][j-i]; else dp[i][j]=dp[i-1][j]; dp[i][j] %= mod; } }
问题:n种物品,每种无区别,第i种有ai个,从中取出m个,取法对于mod取余
dp[i][j] 表示前i种物品共取j件, 则
dp[i+1][j] = { dp[i][j-k] | (0<=k<=min(j, a[i-1]) };
= dp[i+1][j-1]+dp[i][j]-dp[i][j-1-a[i]]
第一项 前i件物品取j-1件;
第二项 第i件物品取0件,前i-1件物品取j件;
第三项 第i-1件物品取0件,前i-2件物品取j-1-a[i-1]件
int n, m, mod; void solve() { for(int i=0; i<=n; i++) //一个都不取方法只有1 dp[i][0]=1; for(int i=0; i<n; i++) for(int j=1; j<=m; j++) { if(j-1>=a[i]) dp[i+1][j] = (dp[i+1][j-1]+dp[i][j]-dp[i][j-1-a[i]]+mod)%mod; else dp[i+1][j] = (dp[i+1][j-1]+dp[i][j])%mod; } }
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1176
思路: dp[i][j]表示在第i秒内在j位置时收到馅饼数量的最大值。( dp题目这里就是关键,想好状态怎么表示。)
map[i][j]表示在第i秒内在j位置时的馅饼数。
状态方程: dp[i][j]= max(dp[i+1][j-1], dp[i+1][j], dp[i+1][j+1]) + map[i][j];
代码:
#include <iostream> #include <cstdio> #include <cstring> using namespace std; #define max(a, b) (a>b?a:b) #define maxx(a, b, c) (max(a, (max(b, c)))) int dp[100002][12];//因为map数组每一个点只用一次,so 用dp数组表示map数组和dp数组 int n; int main() { while(scanf("%d", &n) && n) { int i, x, t, maxt=0; memset(dp, 0, sizeof(dp)); for(i=1; i<=n; i++) { scanf("%d%d", &x, &t); dp[t][x+1] += 1; //这样对于0就不用的特殊处理 if(t>maxt) maxt=t; } for(i=maxt-1; i>0; i--) //从最底层 for(t=1; t<=11; t++) //t=0时不用特殊处理就是从1开始优点 dp[i][t] += maxx(dp[i+1][t-1], dp[i+1][t], dp[i+1][t+1]); printf("%d\n", maxx(dp[1][5], dp[1][6], dp[1][7])); } return 0; }

浙公网安备 33010602011771号