背个背包

OPEN

2019-09-14的沈阳网络赛过去很多天了(8天),现在才来针对里面的C题Dawn-K's water虽然有点晚,但是前有某数论降幂,后有上海网络赛签到提前缀和崩掉······

不管怎么说,由于这次完全背包题的爆炸,我们终于决定要向DP迈进了,为了明年的蓝桥杯和ICPC/CCPC加油?!

              

一、0/1背包问题

1. 洛谷P1060一道板子题,0/1背包昨晚看得脑壳疼还没看懂,可能是大脑这天用太久了,今早看李煜东的书终于大雾?,感动

我比较害怕以后又忘了原理,但是自己解释得也不一定比李同学好(主要是懒)

以后忘了的话就扇自己一巴掌然后再看看李的书吧╮(╯▽╰)╭(网上博客还真是没看懂)

但事实是这个板子不是最优!!!emmm李的书还是不太全这个,自己盘里的PDF更细一些

最优的板子在这个博客里找叭~(常数优化见洛谷最近提交码)?

Code

 1 #pragma warning (disable:4996)
 2 #include <algorithm>
 3 #include <iostream>
 4 #include <iomanip>
 5 #include <cstring>
 6 #include <string>
 7 #include <cstdio>
 8 #include <cmath>
 9 #define MS(x) memset(x,0,sizeof(x))
10 using namespace std;
11 typedef long long ll;
12 typedef unsigned long long ull;
13 const int maxn = 1e8 + 5;
14 using namespace std;
15 
16 int v[30], w[30], f[30005];
17 //0-1背包,钱数代表容量,v价格*w重要度代表价值
18 int main()
19 {
20     int N, m;
21     scanf("%d %d", &N, &m);
22     int vi, wi;
23     for (int i = 1; i <= m; i++)
24     {
25         scanf("%d %d", &v[i], &w[i]);
26         w[i] *= v[i];
27     }
28     for (int i = 1; i <= m; i++)
29     {
30         for (int j = N; j >= v[i]; j--)
31             f[j] = max(f[j - v[i]] + w[i], f[j]);
32     }
33     printf("%d", f[N]);
34     return 0;
35 }
0-1 template

2. 洛谷P1064: 主角还是金明小朋友,0/1背包的拓展题,可能会让人望而却步,事实上储存思路好了就一样easy

Code

 1 #pragma warning (disable:4996)
 2 #include <algorithm>
 3 #include <iostream>
 4 #include <vector>
 5 #include <iomanip>
 6 #include <cstring>
 7 #include <string>
 8 #include <cstdio>
 9 #include <map>
10 #include <stack>
11 #include <cmath>
12 #define MS(x) memset(x,0,sizeof(x))
13 using namespace std;
14 typedef long long ll;
15 typedef unsigned long long ull;
16 const int maxn = 1e8 + 5;
17 using namespace std;
18 int fw[100][3], fv[65][3], f[33000];
19 int main()
20 {
21     int N, m;
22     scanf("%d %d", &N, &m);
23     int v, w, q;
24     for (int i = 1; i <= m; i++)
25     {
26         scanf("%d %d %d", &w, &v, &q);
27         if (!q)//the main part
28         {
29             fw[i][q] = w;
30             fv[i][q] = w * v;
31         }
32         else if (!fw[q][1])//1st attachment
33         {
34             fw[q][1] = w;
35             fv[q][1] = w * v;
36         }
37         else//2ed attachment
38         {
39             fw[q][2] = w;
40             fv[q][2] = w * v;
41         }
42     }
43     for (int i = 1; i <= m; i++)
44     {
45         for (int j = N; j >= 0; j--)
46         {
47             //main part
48             if (j >= fw[i][0])
49                 f[j] = max(f[j], f[j - fw[i][0]] + fv[i][0]);
50             //main + 1st
51             if (j >= (fw[i][0] + fw[i][1]))
52                 f[j] = max(f[j], f[j - fw[i][0] - fw[i][1]] + fv[i][1] + fv[i][0]);
53             //main + 2ed
54             if (j >= (fw[i][0] + fw[i][2]))
55                 f[j] = max(f[j], f[j - fw[i][0] - fw[i][2]] + fv[i][2] + fv[i][0]);
56             //main + 1st + 2ed
57             if (j >= (fw[i][0] + fw[i][1] + fw[i][2]))
58                 f[j] = max(f[j], f[j - fw[i][0] - fw[i][1] - fw[i][2]] + fv[i][0] + fv[i][1] + fv[i][2]);
59         }
60     }
61     printf("%d\n", f[N]);
62     return 0;
63 }
0-1 Pro

              

二、完全背包问题

1. Dawn-K's water:开头肯定就是沈阳网络赛这道啦!(什么?先给板子题?是男人就直接正面干!)

Code

 1 #pragma warning (disable:4996)
 2 #include <algorithm>
 3 #include <iostream>
 4 #include <iomanip>
 5 #include <cstring>
 6 #include <string>
 7 #include <cstdio>
 8 #include <cmath>
 9 #define inf 0X7f7f7f7f
10 #define MS0(x) memset(x,0,sizeof(x)
11 #define MSI(x) memset(x,inf,sizeof(x))
12 using namespace std;
13 typedef long long ll;
14 typedef unsigned long long ull;
15 const int maxn = 1e8 + 5;
16 using namespace std;
17 int w[1005], v[1005];
18 ll f[10005];//注意长度int你就等着WA嘤
19 int main()
20 {
21     int n, m;
22     while (scanf("%d %d", &n, &m) != EOF)
23     {
24         MSI(f);//每次更新f[],由于是背包取价值最小,故以inf初始化
25         f[0] = 0;
26         for (int i = 1; i <= n; i++)
27             scanf("%d %d", &v[i], &w[i]);
28         for (int i = 1; i <= n; i++)
29             for (int j = w[i]; j <= 10000; j++)
30                 f[j] = min(f[j], f[j - w[i]] + v[i]);//同取价值最小用min()
31         //得到全部f[]后为了求出购买的重量(>=背包容量m),采用从m到最大值枚举的办法求出最小价值并得到此时的重量i
32         ll ans = inf, ans2 = 0;
33         for (int i = m; i <= 10000; i++)
34         {
35             if (ans >= f[i])
36             {
37                 ans = f[i];
38                 ans2 = i;
39             }
40         }
41         printf("%lld %lld\n", ans, ans2);
42     }
43     return 0;
44 }
DP完全背包

个虽说也有模板成分,但是由此出来的模板拓展要记得理解嗷~

万岁我用三天就解决了?比数论某降幂轻松多了

2. Jury Compromise这道题要是日后能自个儿码出来我觉得那时我还是很强的,网上有很多错解,参考博客:CNBLOGSCSND

代码码了3天,算是很良心了,知识精化在注释里,仔细看看挺好,事实上我觉得这玩意儿算01背包hh(虚脱orz)

Code

 1 #pragma warning (disable:4996)
 2 #include <algorithm>
 3 #include <iostream>
 4 #include <vector>
 5 #include <iomanip>
 6 #include <cstring>
 7 #include <string>
 8 #include <cstdio>
 9 #include <map>
10 #include <stack>
11 #include <cmath>
12 #define inf 0X7f7f7f7f
13 #define MS_I(x) memset(x,-inf,sizeof(x))
14 #define MS(x) memset(x,0,sizeof(x))
15 #define MSI(x) memset(x,inf,sizeof(x))
16 using namespace std;
17 typedef long long ll;
18 typedef unsigned long long ull;
19 const int maxn = 1e8 + 5;
20 using namespace std;
21 int p[201], d[201];
22 int f[201][801];//20*20*2//我们把m作为v1,p-d作为V2,并考虑到负数下标的问题,所以设大小为800起
23 //f[i][j] = k表示在到已经选择了i个候选人且(P-D)Weiht为j时P+D的Value
24 int ans[21], path[201][21][801];//该题网上Blog所有path为二维而非三维的题解全是错的了
25 int main()
26 {
27     int n, m;
28     int t = 0;
29     while (scanf("%d %d", &n, &m) && (n != 0 || m != 0))
30     {
31         t++;
32         //记得更新嗷
33         MS_I(f);
34         MS(p);
35         MS(d);
36         MS(ans);
37         //path三维路径目前题解里有三维数组和二维+vector容器(超省内存,省一半时间)两种存储办法,这里我们用经典三维数组更能直观体现本质三维dp,故将vector注释
38         MS(path);
39         //vector<int> path[21][801];
40         int range = m * 20;
41         f[0][range] = 0;//原f[0][0]=0在V2下标问题的处理下也随之变成f[0][m*20] = 0
42         for (int i = 1; i <= n; i++)
43             scanf("%d %d", &p[i], &d[i]);
44         for (int i = 1; i <= n; i++)
45             for (int j = m; j >= 1; j--)//逆序(每人最多选一次)(01背包实锤)
46                 //(题解里这个是f[j][k + p[i] - d[i]] <= f[j - 1][k] + p[i] + d[i],省去了我这里多出的越界判定,算是一个很好的小优化
47                 //(不过我是为了按传统套路来硬是WA了n遍才终于改对了!)
48                 //关于 k = p[i] - d[i] && k - p[i] + d[i] <= 2 * range是由于k - p[i] + d[i]可能越界因此不存在该情况加的判定
49                 for (int k = p[i] - d[i]; k <= range * 2; k++)
50                 {
51                     path[i][j][k] = path[i - 1][j][k];
52                     //而根据之前初始化-inf,故f[j - 1][k - p[i] + d[i]] < 0亦说明不存在该情况排去
53                     if (f[j - 1][k - p[i] + d[i]] >= 0 && k - p[i] + d[i] <= 2 * range)//后者没加就WA哭
54                     {
55                         if (f[j][k] <= f[j - 1][k - p[i] + d[i]] + p[i] + d[i])
56                         {
57                             f[j][k] = f[j - 1][k - p[i] + d[i]] + p[i] + d[i];
58                             path[i][j][k] = i;
59                         }
60                     }
61                 }
62         int minIndex, mk;
63         //得到abs(P - D)最小值k
64         for (mk = 0; mk <= range; mk++)
65             if (f[m][range - mk] >= 0 || f[m][range + mk] >= 0)
66                 break;
67         (f[m][range - mk] > f[m][range + mk]) ? minIndex = range - mk : minIndex = range + mk;
68         //目前f[m][minIndex] = P + D; minIndex = P - D + 20 * m
69         //但是不能以为P = (f[m][minIndex] + k) / 2, D同理,因为k自带绝对值属性...所以还是老老实实...
70         cout << "Jury #" << t << endl << "Best jury has value ";
71         cout << (f[m][minIndex] + minIndex - range) / 2 << " for prosecution and value ";
72         cout << (f[m][minIndex] - minIndex + range) / 2 << " for defence:" << endl;
73         //以下三行为vector方法代码(超短!)由于按照顺序遍历,最后的路径本身就是有序的,所以直接输出就可以了
74         //int tmp = m;
75         //for (int i = 0; i < m; i++)
76         //    cout << " " << path[m][minIndex][i];
77         //下面开始演示三维数组path递归,事实上很好理解的嘛,从后往前
78         for (int i = n, j = m, k = minIndex; j >= 1;)
79         {
80             int r = path[i][j][k];
81             ans[j] = r;
82             k -= p[r] - d[r];
83             j--;
84             i = path[r - 1][j][k];
85         }
86         for (int i = 1; i <= m; i++)
87             cout << " " << ans[i];
88         printf("\n\n");
89     }
90     return 0;
91 }
注释比代码多系列

三、多重背包问题

 

转载于:https://www.cnblogs.com/xzmxiao/p/11569688.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值