1、hdu 1260 Tickets
题意:有k个人,售票员可以选择一个人卖,或者同时卖给相邻的两个人。问最少的售票时间。
思路:dp[i] = min(dp[i - 1] + singlep[i], dp[i - 2] + dbp[i - 1]);dp[i]表示卖到第i个人后所需最少时间。注意时间为12小时制。
1 #include<iostream> 2 #include<memory.h> 3 #include<algorithm> 4 using namespace std; 5 int n, k; 6 const int maxk = 2100; 7 int dp[maxk]; 8 int singlep[maxk]; 9 int dbp[maxk]; 10 int main() 11 { 12 scanf("%d", &n); 13 while (n--) 14 { 15 scanf("%d", &k); 16 for (int i = 1; i <= k; i++) scanf("%d", &singlep[i]); 17 for (int i = 1; i <= k - 1; i++) scanf("%d", &dbp[i]); 18 memset(dp, 0, sizeof(dp)); 19 for (int i = 1; i <= k; i++) 20 { 21 if (i == 1)dp[i] = singlep[i]; 22 else 23 { 24 dp[i] = min(dp[i - 1] + singlep[i], dp[i - 2] + dbp[i - 1]); 25 } 26 } 27 int hh = 8; 28 int mm = 0, ss = 0; 29 hh += dp[k]/3600; 30 mm += dp[k]%3600/ 60; 31 ss += dp[k] % 60; 32 char t1[4] = "am", t2[4] = "pm"; 33 printf("%02d:%02d:%02d %s ", (hh<=12?hh:hh-12), mm, ss, (hh >= 13 ? t2 : t1)); 34 } 35 return 0; 36 }
2、hdu 1160 FatMouse's Speed
题意:给出若干只老鼠的重量和速度。要求找到一个最长的序列,里面每只老鼠重量严格递增,速度严格递减。
思路:先按照速度递减排序,然后求重量递增的最长递增子序列。这里采用DP的方法,用pre数组记录前驱。
1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 const int maxn = 1010; 5 struct node 6 { 7 int w; 8 int v; 9 int id; 10 }mice[maxn]; 11 int dp[maxn]; 12 int pre[maxn]; 13 bool Cmp(const node&a, const node&b) 14 { 15 return a.v > b.v; 16 } 17 void Print(int r,int num) 18 { 19 if (r != -1) 20 { 21 Print(pre[r],num+1); 22 printf("%d ", mice[r].id); 23 } 24 else printf("%d ", num); 25 } 26 int main() 27 { 28 int cnt = 0; 29 while (~scanf("%d%d", &mice[cnt].w, &mice[cnt].v)) 30 { 31 mice[cnt].id = cnt + 1; 32 cnt++; 33 } 34 sort(mice, mice + cnt, Cmp); 35 for (int i = 0; i < cnt; i++) dp[i] = 1, pre[i] = -1; 36 int re = 0,maxl=0; 37 for (int i = 0; i < cnt; i++) 38 { 39 int ans = 0,pos=i; 40 for (int j = 0; j < i; j++) 41 { 42 if (dp[j]+1 > dp[i]&&mice[i].w > mice[j].w&&mice[i].v<mice[j].v) 43 { 44 dp[i] = dp[j] + 1; 45 pre[i] = j; 46 pos = j; 47 } 48 } 49 if (dp[i] > maxl) maxl = dp[i], re = i; 50 } 51 Print(re,0); 52 return 0; 53 }
3、poj 1015 Jury Compromise
题意:有n个候选的陪审团的人,需要从中选出m个人,使得这m个人的辩护方总分减去控诉方总分的差值最小,当最小的有多个时,选择辩护方总分加上控诉方总分最高的方案。
思路:dp[i][j]表示选i个人、差值为j-fix中辩护方总分加上控诉方总分最高的方案。用pre[i][j]记录路径。细节见代码。
1 #include<cstdio> 2 #include<ctype.h> 3 #include<algorithm> 4 #include<iostream> 5 #include<cstring> 6 #include<vector> 7 using namespace std; 8 int dp[21][801];//dp(j, k)表示,取j 个候选人,使其辩控差为k 的所有方案中,辩控和最大的那个方案(该方案称为“方案dp(j, k)”)的辩控和。 9 vector<int> path[21][801]; 10 11 int main() 12 { 13 int times = 1; 14 // freopen("input.txt","r",stdin); 15 // freopen("output.txt","w",stdout); 16 int subtraction[201], _plus[201]; 17 int n, m, i, j, k; 18 while (~scanf("%d%d", &n, &m) && n && m) 19 {//一共有n个候选人,要选m个 20 for (i = 0; i<m; ++i)//清空vector 21 for (j = 0; j<801; ++j) 22 path[i][j].clear(); 23 memset(dp, -1, sizeof(dp)); 24 int d, p; 25 for (i = 0; i < n; i++) 26 { 27 cin >> d >> p;//输入辩护方和控诉方的打分 28 subtraction[i] = d - p;//得到每个人的辩护方的分数-控诉方的分数 29 _plus[i] = d + p;//得到和 30 } 31 int fix = 20 * m;//由于题目中辩控差的值k 可以为负数,而程序中数租下标不能为负数,所以,在程序中不妨将辩控差的值都加上修正值fix=400,以免下标为负数导致出错。 32 //为什么fix = 400?这是很显然的,m上限为20人,当20人的d均为0,p均为20时,会出现辨控差为 - 400。修正后回避下标负数问题,区间整体平移,从[-400, 400]映射到[0, 800]。 33 dp[0][fix] = 0; 34 for (k = 0; k < n; k++)//顺序选择一个候选人 35 for (i = m - 1; i >= 0; i--)//进行逆推 36 { 37 for (j = 0; j < 2 * fix; j++) 38 { 39 if (dp[i][j] >= 0) 40 { 41 if (dp[i + 1][j + subtraction[k]] <= dp[i][j] + _plus[k]) 42 {//可行方案dp(j-1, x)能演化成方案dp(j, k)的必要条件是:存在某个候选人i,i 在方案dp(j-1, x)中没有被选上,且x+V(i) = k。在所有满足该必要条件的dp(j-1, x)中,选出 dp(j-1, x) + S(i) 的值最大的那个,那么方案dp(j-1, x)再加上候选人i,就演变成了方案 dp(j, k)。 43 //dp[i][j + d[k]] = max(dp[i][j + d[k]], dp[i-1][j] + s[k]) 44 dp[i + 1][j + subtraction[k]] = dp[i][j] + _plus[k]; 45 path[i + 1][j + subtraction[k]] = path[i][j];//每次更新都要把path全部复制过来,就是因为这个才用的vector 46 path[i + 1][j + subtraction[k]].push_back(k); 47 } 48 } 49 } 50 } 51 //DP后,从第m行的dp(m, fix)开始往两边搜索最小|D-P| 即可,第一个不为dp[m][k]!=-1的位置k就是最小|D-P|的所在。 52 //D+P = dp(m, |D-P| ) ,|D-P|已知。 53 //那么D = (D + P + | D - P | ) / 2, P = (D + P - | D - P | ) / 2,计算D和P时注意修正值fix 54 for (i = 0; dp[m][fix + i] == -1 && dp[m][fix - i] == -1; i++); 55 int temp = (dp[m][fix + i] > dp[m][fix - i]) ? i : -i; 56 int sumD = (dp[m][fix + temp] + temp) / 2; 57 int sumP = (dp[m][fix + temp] - temp) / 2; 58 printf("Jury #%d ", times++); 59 printf("Best jury has value %d for prosecution and value %d for defence: ", sumD, sumP); 60 for (i = 0; i < m; i++) 61 printf(" %d", path[m][fix + temp][i] + 1); 62 printf(" "); 63 64 } 65 return 0; 66 }
4、hdu 1159 Common Subsequence
题意:求两个字符串的最长公共子序列的长度。
思路:DP模板题。dp[i][j]表示a[0]~a[i]和b[0]~b[j]的最长公共子序列的长度。dp[i][j]=dp[i-1][j-1]+1(当a[i]==b[j]);dp[i][j]=max(dp[i-1][j],dp[i][j-1])(当a[i]!=b[j])
1 #include<iostream> 2 #include<algorithm> 3 #include<string.h> 4 using namespace std; 5 const int maxl = 1010; 6 char a[maxl]; 7 char b[maxl]; 8 int dp[maxl][maxl]; 9 int main() 10 { 11 while (~scanf("%s%s", a, b)) 12 { 13 int la = strlen(a); 14 int lb = strlen(b); 15 memset(dp, 0, sizeof(dp)); 16 for (int i = 0; i < la; i++) 17 { 18 for (int j = 0; j < lb; j++) 19 { 20 if (a[i] == b[j]) 21 { 22 if (i > 0 && j > 0)dp[i][j] = dp[i - 1][j - 1] + 1; 23 else dp[i][j] = 1; 24 } 25 else 26 { 27 if(i>0&&j>0)dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); 28 else if (i > 0) dp[i][j] = dp[i - 1][j]; 29 else if (j > 0) dp[i][j] = dp[i][j - 1]; 30 else dp[i][j] = 0; 31 } 32 } 33 } 34 printf("%d ", dp[la - 1][lb - 1]); 35 } 36 return 0; 37 }
5、poj 1661 Help Jimmy
题意:“是男人就下一百层”的超级简化版。限制每次从平台跳下的最大高度差,求从给定的位置出发,到地面的最短时间。
思路:dp[i][0]表示从第i层左侧跳到地面的最短时间,dp[i][1]表示从第i层右侧跳到地面的最短时间。从底向上DP,对于当前层i,从i-1层到第1层更新其最短时间。
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 struct node 8 { 9 int x1;//板子左侧 10 int x2;//板子右侧 11 int h;//所在高度 12 }; 13 node op[1010]; 14 int dp[20010][2]; 15 //dp[i][0]表示以i号平台左边为起点到地面的最短时间,dp[i][1]表示以i号平台右边为起点到地面的最短时间 16 bool cmp(node a, node b) 17 {//按高度从低到高 18 return a.h < b.h; 19 } 20 const int inf = 0x7f7f7f7f; 21 int main() 22 { 23 int t; 24 int n, x, y, maxn; 25 scanf("%d", &t); 26 while (t--) 27 { 28 scanf("%d%d%d%d", &n, &x, &y, &maxn); 29 for (int i = 1; i <= n; i++) 30 { 31 scanf("%d%d%d", &op[i].x1, &op[i].x2, &op[i].h); 32 } 33 sort(op + 1, op + n + 1, cmp); 34 35 // 0 左 36 // 1 右 37 dp[1][0] = dp[1][1] = op[1].h; 38 op[n + 1].x1 = op[n + 1].x2 = x; 39 op[n + 1].h = y;// op[n+1] 其实代表的是坐标(x,y),最高的开始跳的地方; 40 41 for (int i = 2; i <= n + 1; i++) 42 {//从高度低的向上DP 43 dp[i][0] = dp[i][1] = inf; 44 int flag0 = 0; // 用于标记左面端点 45 int flag1 = 0; // 用于标记右面端点 46 int tmp; 47 for (int j = i - 1; j>0; j--) // 这里注意必须逆序,因为从上往下跳,有板子挡住的话就没法继续往下跳了。 48 { 49 if (op[i].h - op[j].h > maxn) continue; 50 if (op[j].x1 <= op[i].x1 && op[j].x2 >= op[i].x1 &&flag0 == 0) 51 {//可以从第i个平台的左侧跳到第j个平台上 52 tmp = min(dp[j][0] - op[j].x1 + op[i].x1, dp[j][1] - op[i].x1 + op[j].x2); 53 dp[i][0] = min(dp[i][0], tmp + op[i].h - op[j].h); 54 //tmp表示跳到第j个平台后向左或向右走到地面的时间的最小值 55 //tmp + op[i].h - op[j].h为再加上从第i个平台左侧跳到第j个平台所需要的时间 56 flag0 = 1; // 表示已经不能继续往下跳了,因为已经有板子了 57 } 58 if (op[j].x1 <= op[i].x2 && op[j].x2 >= op[i].x2&&flag1 == 0) 59 {//可以从第i个平台的右侧跳到第j个平台上 60 tmp = min(dp[j][0] - op[j].x1 + op[i].x2, dp[j][1] - op[i].x2 + op[j].x2); 61 dp[i][1] = min(dp[i][1], tmp + op[i].h - op[j].h); 62 flag1 = 1; // 道理同上 63 } 64 if (flag0 == 1 && flag1 == 1)break; 65 } 66 if (flag0 == 0 && op[i].h <= maxn) 67 {//平台i左侧下没有其他板子,并且距离地面的高度小于等于跳的高度 68 dp[i][0] = min(dp[i][0], op[i].h); 69 } 70 if (flag1 == 0 && op[i].h <= maxn) 71 {//平台i右侧下没有其他板子,并且距离地面的高度小于等于跳的高度 72 dp[i][1] = min(dp[i][1], op[i].h); 73 }// 这里判断是不是可以直接跳到地面上。 74 //cout << dp[i][0] << " " << dp[i][1] << endl; 75 } 76 int cc = min(dp[n + 1][0], dp[n + 1][1]); 77 cout << cc << endl; 78 } 79 return 0; 80 }
6、poj 2533 Longest Ordered Subsequence
题意:给出一个数字序列,求最长递增子序列的长度。
思路:模板题。
1 #include<iostream> 2 #include<vector> 3 #include<algorithm> 4 using namespace std; 5 const int maxn = 1010; 6 int num[maxn]; 7 int pre[maxn]; 8 int dp[maxn]; 9 int n; 10 int DPn2() 11 { 12 memset(pre, -1, sizeof(pre)); 13 dp[0] = 1; 14 int ans = 1; 15 for (int i = 1; i < n; i++) 16 { 17 dp[i] = 1; 18 for (int j = i - 1; j >= 0; j--) 19 { 20 if (num[i] > num[j] && dp[j] + 1 > dp[i]) 21 { 22 dp[i] = dp[j] + 1; 23 pre[i] = j; 24 } 25 } 26 if (dp[i] > ans) ans = dp[i]; 27 } 28 return ans; 29 } 30 int Vnlogn() 31 { 32 vector<int>v; 33 for (int i = 0; i < n; i++) 34 { 35 if (v.empty()) v.push_back(num[i]); 36 else if (num[i] > v.back()) v.push_back(num[i]); 37 else 38 { 39 int pos = lower_bound(v.begin(), v.end(), num[i]) - v.begin(); 40 v[pos] = num[i]; 41 } 42 } 43 return v.size(); 44 } 45 int main() 46 { 47 while (~scanf("%d", &n)) 48 { 49 for (int i = 0; i < n; i++) scanf("%d", &num[i]); 50 printf("%d ", DPn2()); 51 //printf("%d ", Vnlogn()); 52 } 53 return 0; 54 }
7、poj 3186 Treats for the Cows
题意:有n个物品,每次可以从首或者尾选择商品卖出,卖出的价格为商品的价值*年份(初始时每个商品年份都为1,每卖出一个商品后剩下商品的年份+1)。问最后最大的收益值。
思路:dp[i][j]表示卖完第i到第j的最大收益。则dp[j][j + l - 1] = max(dp[j + 1][j + l - 1] + num[j] * (n - l + 1), dp[j][j + l - 2] + num[j+l-1] * (n - l + 1));l为当前枚举的区间长度,n-l+1表示其是第n-l+1个卖出。
1 #include<iostream> 2 #include<memory.h> 3 #include<algorithm> 4 using namespace std; 5 const int maxn = 2010; 6 int num[maxn]; 7 int dp[maxn][maxn];//[i][j]表示从第i到第j最大的收益。 8 int main() 9 { 10 int n; 11 while (~scanf("%d", &n)) 12 { 13 memset(dp, 0, sizeof(dp)); 14 for (int i = 1; i <= n; i++) 15 { 16 scanf("%d", &num[i]); 17 dp[i][i] =num[i]* n; 18 } 19 for (int l=2;l<=n;l++) 20 { 21 for (int j = 1; j+l-1 <= n; j++) 22 { 23 dp[j][j + l - 1] = max(dp[j + 1][j + l - 1] + num[j] * (n - l + 1), dp[j][j + l - 2] + num[j+l-1] * (n - l + 1)); 24 } 25 } 26 printf("%d ", dp[1][n]); 27 } 28 return 0; 29 }
8、hdu 2859 Phalanx
题意:给出n*n的矩阵,求最大对称子矩阵。
思路:dp[i][j]表示左下角为(i,j)的对称矩阵的维数。可以从dp[i-1][j+1]转换。
1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 int n; 5 const int maxn = 1010; 6 char mp[maxn][maxn]; 7 int dp[maxn][maxn];//dp[i][j]表示左下角为(i,j)的对称矩阵的维数。可以从dp[i-1][j+1]转换 8 int main() 9 { 10 while (~scanf("%d", &n)) 11 { 12 if (n == 0)break; 13 for (int i = 1; i <= n; i++) 14 { 15 for (int j = 1; j <= n; j++) cin >> mp[i][j]; 16 } 17 int ans = 1; 18 for (int i = 1; i <= n; i++) 19 { 20 for (int j = n; j >= 1; --j) 21 { 22 dp[i][j] = 1; 23 if (i == 1 || j == n) continue; 24 int ll = dp[i - 1][j + 1]; 25 for (int k = 1; k <= ll; k++) 26 { 27 if (mp[i - k][j] == mp[i][j + k]) dp[i][j]++; 28 else break; 29 } 30 ans = max(ans, dp[i][j]); 31 } 32 } 33 printf("%d ", ans); 34 } 35 return 0; 36 }
9、poj 3616 Milking Time
题意:共有n个小时,给出一些划分时间区间及区间内挤奶的效率,每次挤完奶后员工必须休息R小时,问如何安排,使得最后总的效率最大。
思路:DP[i]表示到时刻i为止的最大效率。哎,好不容易一次不看题解自己推出dp过程,加油233
1 #include<iostream> 2 #include<algorithm> 3 #include<memory.h> 4 using namespace std; 5 int n, m, r; 6 struct node 7 { 8 int st; 9 int ed; 10 int f; 11 }pds[1010]; 12 int dp[2000010]; 13 bool Cmp(const node&a, const node&b) 14 { 15 if (a.st == b.st)return a.ed < b.ed; 16 else return a.st < b.st; 17 } 18 int main() 19 { 20 while (~scanf("%d%d%d", &n, &m, &r)) 21 { 22 for (int i = 1; i <= m; i++) 23 { 24 scanf("%d%d%d", &pds[i].st, &pds[i].ed, &pds[i].f); 25 } 26 27 sort(pds+1, pds + m+1, Cmp); 28 pds[0].st = 0, pds[0].ed = 0, pds[0].f = 0; 29 memset(dp, 0, sizeof(dp)); 30 int ans = 0; 31 for (int i = 1; i <= m; i++) 32 { 33 int ed = pds[i].ed + r; 34 int ee = pds[i].st; 35 if (ed > n + r)continue; 36 for (int j = 0; j < i; j++) 37 { 38 int te; 39 if (j == 0) te = pds[j].ed; 40 else te = pds[j].ed + r; 41 if (te <= ee) 42 { 43 dp[ed] = max(dp[ed], dp[te] + pds[i].f); 44 ans = max(dp[ed], ans); 45 } 46 } 47 } 48 printf("%d ", ans); 49 } 50 return 0; 51 }
10、hdu 2955 Robberies
题意:给出最大被抓概率maxp和银行数目,以及每个银行的能偷的钱财以及被抓的概率,求最大能偷到的钱数。
思路:dp[i]表示偷当前钱数的最大不被抓的概率,那么取不被抓概率大于1-maxp的最大i即为答案。
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 int val[110]; 6 double w[110]; 7 double dp[10010]; 8 int main() 9 { 10 int t; 11 scanf("%d", &t); 12 while (t--) 13 { 14 double maxw;//被抓的概率 15 int n; 16 int sum = 0; 17 scanf("%lf%d", &maxw, &n); 18 for (int i = 0; i < n; i++) scanf("%d%lf", val + i, w + i),sum+=val[i]; 19 int maxans = 0; 20 memset(dp, 0, sizeof(dp)); 21 dp[0] = 1; 22 for (int i = 0; i < n; i++) 23 { 24 for (int v = sum; v >= val[i]; v--) 25 { 26 dp[v] = max(dp[v], dp[v - val[i]] *(1-w[i]));//当前偷价值为v时不被抓的概率 27 if (dp[v] > 1 - maxw) maxans = max(maxans, v); 28 } 29 } 30 printf("%d ", maxans); 31 } 32 return 0; 33 }
11、poj 2923 Relocation
题意:有两辆卡车,有各自的最大载重c1,c2,有n件家具,现在需要把这些家具从一个地方搬到另一个地方,问最少的搬运次数。
思路:先状态压缩,确定所有可行的状态(所选择的货物能够在一趟内被搬运),然后对所有可行状态进行01背包(注意,转移的条件要求两种相应的状态不能有交集<即每种货物最多只会被搬运一次>),得到解。
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring> 5 using namespace std; 6 const int maxst = 1 << 11; 7 const int INF = 0x3f3f3f3f; 8 const int maxn = 12; 9 const int maxw = 1010; 10 int st[maxst];//储存可行状态 11 int dp[maxst]; 12 bool vis[maxw]; 13 int w[maxn]; 14 int n, c1, c2; 15 bool ok(int cst) 16 { 17 memset(vis, 0, sizeof(vis)); 18 vis[0] = true; 19 int sum = 0; 20 for (int i = 0; i < n; i++) 21 { 22 if ((cst >> i)&1) 23 {//如果要运走当前货物 24 sum += w[i]; 25 for (int j = c1; j >= w[i]; j--) 26 { 27 if (vis[j - w[i]]) vis[j] = true; 28 } 29 } 30 } 31 //if (cst == (1 << n) - 1) printf("%d ", sum); 32 if (sum > c1 + c2) return false; 33 for (int i = 0; i <= c1; i++) 34 { 35 if (vis[i] && sum - i <= c2) return true; 36 } 37 return false; 38 } 39 int main() 40 { 41 int t; 42 scanf("%d", &t); 43 int Case = 1; 44 while (t--) 45 { 46 scanf("%d%d%d", &n, &c1, &c2); 47 for (int i = 0; i < n; i++) scanf("%d", w + i); 48 49 int tot = 0; 50 int maxtot = (1 << n) - 1; 51 for (int i = 1; i <= maxtot; i++) 52 { 53 dp[i] = INF; 54 if (ok(i)) st[tot++] = i; 55 } 56 //对状态进行0/1背包 57 dp[0] = 0; 58 for (int i = 0; i < tot; i++) 59 { 60 for (int j = maxtot; j >= 0; j--) 61 { 62 if (dp[j] == INF) continue; 63 if ((j&st[i]) == 0) 64 { 65 //if ((j | st[i]) == maxtot) printf("%d %d %d %d ", j, st[i], dp[j], dp[st[i]]); 66 dp[j | st[i]] = min(dp[j | st[i]], dp[j] + 1); 67 } 68 } 69 } 70 printf("Scenario #%d: %d ", Case++, dp[maxtot]);//题目保证每个家具都能被运走,不存在dp[maxtot]=INF的情况 71 if (t) printf(" "); 72 } 73 return 0; 74 }
12、hdu 3466 Proud Merchants
题意:每个商人有一个货物,当你的钱不少于Qi时商人才会和你交易。问用M的钱,求买到的货物的最大价值和。
思路:如果要先买A货物,再买B货物,那么首先需要保证Pa+Qb>Pb+Qa,即Qa-Pa<Qb-Pb,这样才能保证dp不会受到影响。
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cstdio> 5 using namespace std; 6 const int maxn = 510; 7 const int maxv = 5010; 8 struct node 9 { 10 int p, q, v; 11 friend bool operator<(const node &a, const node &b) 12 { 13 return a.q-a.p < b.q-b.p; 14 } 15 }item[maxn]; 16 int dp[maxv]; 17 int main() 18 { 19 int n, m; 20 while (~scanf("%d%d", &n, &m)) 21 { 22 for (int i = 0; i < n; i++) scanf("%d%d%d",&item[i].p,&item[i].q,&item[i].v); 23 sort(item, item + n); 24 memset(dp, 0, sizeof(dp)); 25 for (int i = 0; i < n; i++) 26 { 27 for (int j = m; j >= max(item[i].p,item[i].q); j--) 28 { 29 dp[j] = max(dp[j], dp[j - item[i].p] + item[i].v); 30 } 31 } 32 printf("%d ", dp[m]); 33 } 34 return 0; 35 }
13、hdu 2639 Bone Collector II
题意:求01背包第k大值
思路:每个状态保存前k个值,用到多路归并的思想。
1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 int val[110]; 6 int vol[110]; 7 int dp[1100][40]; 8 int pre[40]; 9 int trans[40]; 10 int main() 11 { 12 int t; 13 scanf("%d", &t); 14 while (t--) 15 { 16 int n, v, k; 17 scanf("%d%d%d", &n, &v, &k); 18 for (int i = 0; i < n; i++) scanf("%d", val+i); 19 for (int i = 0; i < n; i++) scanf("%d", vol+i); 20 memset(dp, 0, sizeof(dp)); 21 memset(pre, 0, sizeof(pre)); 22 memset(trans, 0, sizeof(trans)); 23 for (int i = 0; i < n; i++) 24 { 25 for (int tv = v; tv >= vol[i]; tv--) 26 { 27 for (int j = 0; j < k; j++) 28 { 29 pre[j] = dp[tv][j]; 30 trans[j] = dp[tv - vol[i]][j]+val[i]; 31 } 32 int id1 = 0, id2 = 0, cur = 0; 33 while (cur < k && (id1 < k || id2 < k)) 34 { 35 if (pre[id1] >= trans[id2]) dp[tv][cur] = pre[id1++]; 36 else dp[tv][cur] = trans[id2++]; 37 if (cur == 0)cur++; 38 else if (dp[tv][cur] != dp[tv][cur - 1]) cur++; 39 } 40 } 41 } 42 printf("%d ", dp[v][k - 1]); 43 } 44 return 0; 45 }
14、poj 2184 Cow Exhibition
题意:每头牛都有funny值fi和smart值si(有正负),现在需要选出一些牛,使得所有牛的funny值之和TF与smart值之和TS不小于0,同时TS+TF最大。
思路:因为有负值,所以向右偏移;然后求每个对应的TF下的最大TS。如果s[i]大于0,则dp[v]从dp[v-s[i]]转移过来。因为当前容量为正,则相当于从v-s[i]加上s[i],然后加上价值f[i];如果s[i]小于0,则dp[v]从dp[v-s[i]]即dp[v+|s[i]|]转移过来。因为当前容量为负,则相当于从v+|s[i]|减去|s[i]|,然后加上价值f[i].
1 //基本思路:找到当前ts下最大的tf.之后找最大的ts+tf 2 3 #include<iostream> 4 #include<cstring> 5 #include<algorithm> 6 #include<cstdio> 7 using namespace std; 8 int s[110]; 9 int f[110]; 10 const int maxv = 200100; 11 const int INF = 0x3f3f3f3f; 12 int dp[maxv]; 13 int main() 14 { 15 int n; 16 while (~scanf("%d", &n)) 17 { 18 for (int i = 0; i < n; i++) 19 { 20 scanf("%d%d", s + i, f + i); 21 } 22 int sumv =n*2000; 23 memset(dp, -INF, sizeof(dp)); 24 int SHIFT = n*1000; 25 dp[SHIFT] = 0; 26 for (int i = 0; i < n; i++) 27 { 28 if (s[i] >= 0) 29 {/*如果s[i]大于0,则dp[v]从dp[v-s[i]]转移过来。因为当前容量为正,则相当于从v-s[i]加上 30 s[i],然后加上价值f[i]*/ 31 for (int v = sumv; v >= s[i]; v--) dp[v] = max(dp[v], dp[v - s[i]] + f[i]); 32 } 33 else 34 {/*如果s[i]小于0,则dp[v]从dp[v-s[i]]即dp[v+|s[i]|]转移过来。因为当前容量为负,则相当于从v+|s[i]|减去 35 |s[i]|,然后加上价值f[i]*/ 36 for (int v = 0; v <= sumv + s[i]; v++) dp[v] = max(dp[v], dp[v - s[i]] + f[i]); 37 } 38 } 39 int ans = 0; 40 for (int i = SHIFT; i <= sumv; i++) 41 { 42 if(dp[i]>=0)ans = max(ans, dp[i] + i - SHIFT); 43 } 44 printf("%d ", ans); 45 } 46 return 0; 47 }
15、uva 562 Dividing coins
题意:需要把所有硬币分成两堆,使得两堆价值差最小。
思路:设置容量为所有硬币的总价值/2,然后dp.
1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxn = 110; 6 const int maxw = 50010; 7 int dp[maxw]; 8 int w[maxn]; 9 int main() 10 { 11 int t; 12 scanf("%d", &t); 13 while (t--) 14 { 15 int n; 16 scanf("%d", &n); 17 int sum = 0; 18 for (int i = 0; i < n; i++) 19 { 20 scanf("%d", &w[i]); 21 sum += w[i]; 22 } 23 int cmax = sum / 2; 24 memset(dp, 0, sizeof(dp)); 25 for (int i = 0; i < n; i++) 26 { 27 for (int cw = cmax; cw >= w[i]; cw--) 28 { 29 dp[cw] = max(dp[cw], dp[cw - w[i]] + w[i]); 30 } 31 } 32 printf("%d ", sum - 2 * dp[cmax]); 33 } 34 return 0; 35 }
16、uva 624 CD
题意:需要把一些CD刻录在磁带上,问最多能够刻录的时长是多少?
思路:简单01背包,注意记录。
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<vector> 5 #include<cstdio> 6 using namespace std; 7 const int maxn = 20000; 8 int Time[25]; 9 int vis[25][maxn]; 10 int dp[maxn]; 11 int main() 12 { 13 int n, num; 14 while (~scanf("%d", &n)) 15 { 16 scanf("%d", &num); 17 for (int i = 1; i <= num; i++) 18 { 19 scanf("%d", &Time[i]); 20 } 21 memset(dp, 0, sizeof(dp)); 22 memset(vis, -1,sizeof(vis)); 23 for (int i = 1; i <= num; i++) 24 { 25 for (int j = n; j >= Time[i]; j--) 26 { 27 if (dp[j - Time[i]] + Time[i] > dp[j]) 28 { 29 dp[j] = dp[j - Time[i]] + Time[i]; 30 vis[i][j] = i; 31 } 32 } 33 } 34 vector<int>Order; 35 int w = n; 36 for (int i = num; i >= 1; --i) 37 { 38 if (vis[i][w] != -1) 39 { 40 Order.push_back(Time[vis[i][w]]); 41 w -= Time[vis[i][w]]; 42 } 43 } 44 int sz = Order.size(); 45 for (int i = sz - 1; i >= 0; i--) printf("%d ", Order[i]); 46 printf("sum:%d ", dp[n]); 47 } 48 return 0; 49 }
17、hdu 2546 饭卡
题意:当卡上余额大于等于5元时,一定可以购买成功一样菜(无论该菜的价格是否大于5元)。求最小的余额数
思路:先用当前拥有的额度-5元去买除最贵菜之外的菜,再用剩下的钱去买那个最贵的菜。
1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdio> 5 using namespace std; 6 const int maxn = 1010; 7 const int maxm = 1010; 8 int price[maxn]; 9 int dp[maxm]; 10 int main() 11 { 12 int n; 13 while (~scanf("%d", &n) && n) 14 { 15 for (int i = 1; i <= n; i++) scanf("%d", &price[i]); 16 int inim; 17 scanf("%d", &inim); 18 if (inim < 5) 19 { 20 printf("%d ", inim); 21 continue; 22 } 23 inim -= 5;//保留5元,用5元去买最贵的菜 24 sort(price + 1, price + 1 + n); 25 memset(dp, 0, sizeof(dp));//dp[i]表示用i块钱能够买的菜总花费最高 26 for (int i = 1; i <=n-1; i++) 27 { 28 for (int j = inim; j >= price[i]; --j) 29 { 30 dp[j] = max(dp[j], dp[j - price[i]] + price[i]); 31 } 32 } 33 printf("%d ",inim+5-dp[inim]-price[n]); 34 } 35 return 0; 36 }
18、poj 3624 Charm Bracelet
题意:需要串手链,使得总重不超过M的情况下Di之和最大
思路:01背包
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 using namespace std; 5 const int maxn = 3600; 6 const int maxw = 13000; 7 int rat[maxn]; 8 int wt[maxn]; 9 int dp[maxw]; 10 int main() 11 { 12 int n, m; 13 scanf("%d%d", &n, &m); 14 for (int i = 1; i <= n; i++) scanf("%d%d", &wt[i], &rat[i]); 15 for (int i = 1; i <= n; i++) 16 { 17 for (int v = m; v >= wt[i]; v--) 18 { 19 dp[v] = max(dp[v], dp[v - wt[i]] + rat[i]); 20 } 21 } 22 printf("%d ", dp[m]); 23 return 0; 24 }
19、hdu 2602 Bone Collector
题意:需要收集骨头,在不超过背包容量的情况下求最大价值。
思路:01背包
1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdio> 5 using namespace std; 6 const int maxn = 1010; 7 const int maxv = 1010; 8 int vali[maxn]; 9 int voli[maxn]; 10 int dp[maxv]; 11 int main() 12 { 13 int t; 14 int n, mv; 15 scanf("%d", &t); 16 while (t--) 17 { 18 scanf("%d%d", &n, &mv); 19 for (int i = 1; i <= n; i++) scanf("%d", &vali[i]); 20 for (int i = 1; i <= n; i++) scanf("%d", &voli[i]); 21 memset(dp, 0, sizeof(dp)); 22 for (int i = 1; i <= n; i++) 23 { 24 for (int j = mv; j >= voli[i]; j--) 25 { 26 dp[j] = max(dp[j], dp[j - voli[i]] + vali[i]); 27 } 28 } 29 printf("%d ",dp[mv]); 30 } 31 return 0; 32 }