zoukankan      html  css  js  c++  java
  • 【洛谷】训练场_动态规划的背包问题篇(不全)

    P1060开心的金明

    题意:

    金明有n块钱,他有m样东西可买,这m样东西每份都有自己的价格v与重要度(lv:1~5),求在他不超过n元的前提下,使每件物品的价格与重要度的乘积的总和最大。

    分析:

    Dp的话要开一个二维数组,n < 30000, m < 25, 这个二维数组还是可以开出来的。

    接着就是简单的dp模板题了。

     1 int rec(int i, int j)
     2 {
     3     if(dp[i][j] >= 0) return dp[i][j];  //
     4 
     5     int res;
     6     if(i == m) res = 0;    //
     7     else if(j < buy[i].v) res = rec(i + 1, j);  //
     8     else res = max(rec(i + 1, j), rec(i + 1, j - buy[i].v) + buy[i].v * buy[i].l); //
     9 
    10     return dp[i][j] = res;
    11 }
    模板

    这里使用的记忆化搜索以减少搜索时间。

    ①    :判断是否已经搜过这点,是则直接返回;

    ②    :已经搜索完物品

    ③    :背包装不下该物品的重量(这里是价值量),则跳过该物品

    ④    :可以选,比较选与不选哪两者的价值更大,谁大就先记录哪个

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 using namespace std;
     6 const int maxn = 26;
     7 int n, m;
     8 struct node{
     9     int v; // 价值
    10     int l; // 等级
    11 }buy[maxn];
    12 int dp[maxn+1][30005];
    13 int ans, res;
    14 
    15 int rec(int i, int j)
    16 {
    17     if(dp[i][j] >= 0) return dp[i][j];
    18 //    printf("[%d][%d]
    ", i, j);
    19     int res;
    20     if(i == m) res = 0;
    21     else if(j < buy[i].v) res = rec(i + 1, j);
    22     else res = max(rec(i + 1, j), rec(i + 1, j - buy[i].v) + buy[i].v * buy[i].l);
    23 
    24     return dp[i][j] = res;
    25 }
    26 
    27 int main()
    28 {
    29     while(cin >> n >> m){
    30         for(int i = 0; i < m; i++) cin >> buy[i].v >> buy[i].l;
    31         memset(dp, -1, sizeof(dp));
    32 
    33         cout << rec(0, n) << endl;
    34     }
    35     return 0;
    36 }
    AC代码

    下一题:

    P1164小A点菜

    题意:

    Uim与小A去吃饭,这家店的菜式有n种但每种只能选一次,uim有m块钱,保证能把m块钱刚好花完,问有多少种点菜方式。如,n = 4,m = 4,n道菜分别1,1,2,2(元),即有3种选择[1,1,2(3)][1,1,2(4)][2,2]。

    分析:

    本题跪了,因为我还领略到dp的精髓,于是看了题解。

    现在我可以粗略地认为,dp在一些问题上,就是“选与不选”的关系,然后再判断这关系前后的结果问题。

    比如这题:

    点菜只有点与不点的关系,并且数据了保证m块一定会被花完,因此就有以下关系:

    If(j – 第i道菜的价格) dp[i][j] = dp[i-1][j] + 1; // 钱刚好充足,则点菜的方案+1

    这个是钱充足的特例,当钱不刚好充足的时候,即钱比菜钱多的时候有以下关系:

    If(i-第i道菜的价格)dp[i][j] = dp[i-1][j] + f[I-1][j-v[i]]; // 吃这道菜之前的方案数加上吃这道菜的方案数

    如果钱不充足的话,就不吃(白给),与吃第i-1道菜的方案数相同:

    If(i-第i道菜的价格)dp[i][j] = dp[i-1][j];

    最后输出dp[n][m]即可得到AC,因为以上遍历的意思就是:有m钱,对于吃这n道菜的方案数,所以,dp[n][m]就是最终答案。

    感谢洛谷作者 sslzgrh 的题解分析。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 using namespace std;
     5 
     6 int n, m;
     7 int dp[102][10003];
     8 int v[102];
     9 
    10 int main()
    11 {
    12     while(cin >> n >> m){
    13         for(int i = 1; i <= n; i++) cin >> v[i];
    14 
    15         for(int i = 1; i <= n; i++){
    16             for(int j = 1; j <= m; j++){
    17                 if(j == v[i]) dp[i][j] = dp[i-1][j] + 1;
    18                 if(j > v[i]) dp[i][j] = dp[i-1][j] + dp[i-1][j-v[i]];
    19                 if(j < v[i]) dp[i][j] = dp[i-1][j];
    20             }
    21         }
    22         cout << dp[n][m] << endl;
    23     }
    24     return 0;
    25 }
    AC代码

    先暂停下,这时我开始对dp有点头绪了:dp也是搜索的一种,只不过这种搜索未必能把所有情况搜索完毕。解题的关键是找出“是”与“非”二者之间的关系所带来的关系,以及前一种关系与后一种关系之间的关系(这么说是不是已经晕了)。我现在困惑的是,如何才能构造出与表达这种关系相适应的算法?也许这只能多做题了吧。

    下一题:

    P1048采药

    题意:

    辰辰到山里采药,他有T个小时,山里有M株草药,每株草药都有自己所需要花费的时间t与价值v,问在T小时辰辰采到的药草的最大价值总和。

    分析:

    ?????这不就是完全的01背包问题了吗?比开心的金明还要01背包问题,即关系只有采与不采的关系,完全的水题。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 
     6 int T, M;
     7 int t[102];
     8 int v[102];
     9 int dp[102][1002]; // 默认每株草药花费的时间在1~1000
    10 
    11 int main()
    12 {
    13     while(cin >> T >> M){
    14         for(int i = 0; i < M; i++) cin >> t[i] >> v[i];
    15 
    16         for(int i = 0; i < M; i++){
    17             for(int j = 0; j <= T; j++){
    18                 if(j < t[i])
    19                     dp[i+1][j] = dp[i][j];
    20                 else
    21                     dp[i+1][j] = max(dp[i][j], dp[i][j-t[i]] + v[i]);
    22             }
    23         }
    24         cout << dp[M][T] << endl;
    25     }
    26     return 0;
    27 }
    AC代码

    下一题:

    P1049装箱问题

    题意:

    有个容量为V的箱子,并且有n个物品,每个物品有属于自己的体积v,问最后装得的箱子的容量最小。

    分析:

    同样也是01背包问题,只不过最后要还要用总量减去最大值。

    V – dp[n][V]

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 using namespace std;
     5 
     6 int V, n;
     7 int bag[32];
     8 int dp[32][20006];
     9 
    10 int main()
    11 {
    12     while(cin >> V >> n){
    13         for(int i = 0; i < n; i++) cin >> bag[i];
    14 
    15         for(int i = 0; i < n; i++){
    16             for(int j = 0; j <= V; j++){
    17                 if(j < bag[i])
    18                     dp[i+1][j] = dp[i][j];
    19                 else
    20                     dp[i+1][j] = max(dp[i][j], dp[i][j-bag[i]] + bag[i]);
    21             }
    22         }
    23         cout << V - dp[n][V] << endl;
    24     }
    25     return 0;
    26 }
    AC代码

    下一题:

    T1616疯狂的采药

    题意:

    采药一题题意几乎一样,只不过草药可以无限采。

    分析:

    可以无限采的话就变成了完全背包问题,如果数据小的话,这样写就可以AC了

     1 #include<iostream>
     2 #include<cstdio>
     3 using namespace std;
     4 
     5 int T, M;
     6 int t[10005], v[10005];
     7 int dp[100005]
     8 int main()
     9 {
    10     while(cin >> T >> M){
    11         for(int i = 0; i < M; i++) cin >> t[i] >> v[i];
    12 
    13         for(int i = 0; i < M; i++){
    14             for(int j = 0; j <= T; j++){
    15                 if(j < t[i])
    16                     dp[i+1][j] = dp[i][j];
    17                 else
    18                     dp[i+1][j] = max(dp[i][j], dp[i+1][j-t[i]] + v[i]);
    19             }
    20         }
    21         cout << dp[M][T] << endl;
    22     }
    23     return 0;
    24 }
    完全背包问题模板

    但该题数据大的让人恶心,开二维数组是一定会爆的,所以得转换一下(分析贴在代码里):

    感谢洛谷作者神云_cloud的题解:

     1 #include<iostream>
     2 #include<cstdio>
     3 using namespace std;
     4 
     5 int T, M;
     6 int t[10005], v[10005];
     7 int dp[100005];
     8 int main()
     9 {
    10     while(cin >> T >> M){
    11         for(int i = 0; i < M; i++) cin >> t[i] >> v[i];
    12 
    13         for(int i = 0; i < M; i++){
    14             for(int j = t[i]; j <= T; j++){ // 把原来的 j = 0 改成 j = t[i]
    15 //                if(j < t[i])
    16 //                    dp[i+1][j] = dp[i][j];
    17 //                else                          // 这样一来就默认 j <= T 了
    18                     dp[j] = max(dp[j], dp[j-t[i]] + v[i]);
    19             }
    20         }
    21         cout << dp[T] << endl;
    22     }
    23     return 0;
    24 }
    AC代码

    最后小结:

    以上题目都是被洛谷标记为普及难度的dp题,包含了01背包问题完全背包问题,通过这几题的练习可以熟悉dp的模板与特性。不过dp相比贪心算法的思路还是更具有图表型,但有时太固执于图表又会陷入数据过大而不知所措的困境(如疯狂的采药这题)。

    还是那种感觉,dp算法是更多解决“拿“与”不拿“的问题。但数据如果变大了要怎么办?这得要对dp的思维特别熟悉才能够把dp优化。所以,除了会”默写“,更重要的还是要掌握思想呀。

    或许你会觉得我这有些分析写跟没写一样,因为我觉得真的都是模板题或者加一点转变,相信聪明的你熟悉模板的话一看就想到该怎么写了。我是拿着《(第2版)挑战程序设计竞赛》一书边看别学边敲的,所以感觉知道有这样的模板算法,一看题目就知道是水题了quq,着实抱歉。 

    谢谢你能看到最后。

  • 相关阅读:
    nginx学习,下载、安装。使用:正向代理、反向代理、负载均衡
    idea一键导入所有包
    开源小工具-随机生成图片验证码
    记一次Nginx报错403(Permission denied)
    记一次swf视频转mp4经历
    enumerate函数
    filter函数过滤序列
    RetinaNet pytorch implement from scratch 03--Focal Loss
    [读论文]Weighted Boxes Fusion 代替NMS的result ensemble
    Pytorch使用autograd.function自定义op
  • 原文地址:https://www.cnblogs.com/Ayanowww/p/10809896.html
Copyright © 2011-2022 走看看