zoukankan      html  css  js  c++  java
  • 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
    ", (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
    ", 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 }
    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
    ", 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 }
    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
    ", 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
    ", DPn2());
    51         //printf("%d
    ", 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
    ", 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
    ", 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
    ", 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
    ", 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
    ", 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 }
    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
    ", 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
    ", 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
    ", 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
    ", 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
    ", 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
    ", 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 }
    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
    ", 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
    ",dp[mv]);
    30     }
    31     return 0;
    32 }
    View Code
  • 相关阅读:
    分割回文串(力扣第131题)
    子集 II(力扣第91题)
    子集(力扣第78题)
    组合总和 III(力扣第216题)
    JavaWeb部分 (前序)
    JavaSE部分 (IO流下)
    JavaSE部分 (IO流上)
    JavaSE部分 (File类)
    Leetcode 08.04 幂集 回溯与位图
    Leetcode 1405 最长快乐字符串 穷举与贪心
  • 原文地址:https://www.cnblogs.com/ivan-count/p/7337799.html
Copyright © 2011-2022 走看看