DP专题·二

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\n", (hh<=12?hh:hh-12), mm, ss, (hh >= 13 ? t2 : t1));
34     }
35     return 0;
36 }
View Code

 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\n", mice[r].id);
23     }
24     else printf("%d\n", 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 }
View Code

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\n", times++);
59         printf("Best jury has value %d for prosecution and value %d for defence:\n", sumD, sumP);
60         for (i = 0; i < m; i++)
61             printf(" %d", path[m][fix + temp][i] + 1);
62         printf("\n\n");
63 
64     }
65     return 0;
66 }
View Code

 

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\n", dp[la - 1][lb - 1]);
35     }
36     return 0;
37 }
View Code

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 }
View Code

 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\n", DPn2());
51         //printf("%d\n", Vnlogn());
52     }
53     return 0;
54 }
View Code

 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\n", dp[1][n]);
27     }
28     return 0;
29 }
View Code

 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\n", ans);
34     }
35     return 0;
36 }
View Code

 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\n", ans);
49     }
50     return 0;
51 }
View Code

 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\n", maxans);
31     }
32     return 0;
33 }
View Code

 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\n", 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\n%d %d\n", 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:\n%d\n", Case++, dp[maxtot]);//题目保证每个家具都能被运走,不存在dp[maxtot]=INF的情况
71         if (t) printf("\n");
72     }
73     return 0;
74 }
View Code

 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\n", dp[m]);
33     }
34     return 0;
35 }
View Code

 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\n", dp[v][k - 1]);
43     }
44     return 0;
45 }
View Code

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\n", ans);
45     }
46     return 0;
47 }
View Code

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\n", sum - 2 * dp[cmax]);
33     }
34     return 0;
35 }
View Code

 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\n", dp[n]);
47     }
48     return 0;
49 }
View Code

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\n", 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\n",inim+5-dp[inim]-price[n]);
34     }
35     return 0;
36 }
View Code

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\n", dp[m]);
23     return 0;
24 }
View Code

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\n",dp[mv]);
30     }
31     return 0;
32 }
View Code

 

posted @ 2017-10-30 19:30  萌萌的美男子  阅读(205)  评论(0)    收藏  举报