暑假集训的第一天 第二天了。

决定还是先把基础巩固一下好了,万一后面训练赛出事故更尴尬。

所以今天的主题是 —— 01背包及变形

动态规划概念

动态规划Dynamic programming,DP)是一种在数学、计算机科学和经济学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。 动态规划常常适用于有重叠子问题和最优子结构性质的问题,动态规划方法所耗时间往往远少于朴素解法。

动态规划背后的基本思想非常简单。大致上,若要解一个给定问题,我们需要解其不同部分(即子问题),再合并子问题的解以得出原问题的解。 通常许多子问题非常相似,为此动态规划法试图仅仅解决每个子问题一次,从而减少计算量: 一旦某个给定子问题的解已经算出,则将其记忆化存储,以便下次需要同一个子问题解之时直接查表。 这种做法在重复子问题的数目关于输入的规模呈指数增长时特别有用。

动态规划问题满足三大重要性质

最优子结构性质:如果问题的最优解所包含的子问题的解也是最优的,我们就称该问题具有最优子结构性质(即满足最优化原理)。最优子结构性质为动态规划算法解决问题提供了重要线索。

子问题重叠性质:子问题重叠性质是指在用递归算法自顶向下对问题进行求解时,每次产生的子问题并不总是新问题,有些子问题会被重复计算多次。动态规划算法正是利用了这种子问题的重叠性质,对每一个子问题只计算一次,然后将其计算结果保存在一个表格中,当再次需要计算已经计算过的子问题时,只是在表格中简单地查看一下结果,从而获得较高的效率。

无后效性:将各阶段按照一定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策,而只能通过当前的这个状态。换句话说,每个状态都是过去历史的一个完整总结。这就是无后向性,又称为无后效性。

以上转自 https://blog.csdn.net/qq_37774171/article/details/81188690

第一个 dp 相关的专题,所以就直接把概念贴在这方面以后查阅。

题解

[HDU-2602 Bone Collector] 01背包裸题

http://acm.hdu.edu.cn/showproblem.php?pid=2602

就大概是个纯板子,就放这了。

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;const int maxn = 1e5+5;struct node
{int a, c;
}x[maxn];int dp[maxn];int main() {int t;scanf("%d", &t);while(t--) {int n, v;scanf("%d%d", &n, &v);for(int i = 1; i <= n; i++) {scanf("%d", &x[i].a);}for(int i = 1; i <= n; i++) {scanf("%d", &x[i].c);}memset(dp, 0, sizeof(dp));for(int i = 1; i <= n; i++) {for(int j = v; j >= x[i].c; j--) {dp[j] = max(dp[j], dp[j-x[i].c] + x[i].a); }}printf("%d\n", dp[v]);}   return 0;
}

[POJ-3624 Charm Bracelet] 01背包裸题

http://poj.org/problem?id=3624

同上,就不贴代码了。

[HDU-2546 饭卡] 01背包常规

http://acm.hdu.edu.cn/showproblem.php?pid=2546

和上边两道裸题很类似,唯一不同的地方大概就是饭卡内的余额小于5的话不能用,多于5时也可买超过5元的物品。因此需要排序一下,把最大的物品最后“赊账”买掉,大概就是这样。

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;const int maxn = 1e4+5;int a[maxn];
int dp[maxn];int main() {int n, m;while(~scanf("%d", &n) && n) {for(int i = 1; i <= n; i++) {scanf("%d", &a[i]);}scanf("%d", &m);if(m < 5) {printf("%d\n", m);continue;}memset(dp, 0, sizeof(dp));sort(a+1, a+1+n);m = m - 5;  // 用来吃掉最大的一个for(int i = 1; i < n; i++) {for(int j = m; j >= a[i]; j--) {dp[j] = max(dp[j], dp[j-a[i]]+a[i]);}}printf("%d\n", m+5-a[n]-dp[m]);}return 0;
}

[UVA-624 CD] 01背包常规

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=565

同样是常规题型,需要输出背包所取的物品的“路径”,用到递归。

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;const int maxn = 2e3+5;int a[maxn];
int dp[maxn][maxn];void dfs(int n, int m) {if(n == 0) {return ;}if(dp[n][m] == dp[n-1][m]) {dfs(n-1, m);}else {dfs(n-1, m-a[n]);printf("%d ", a[n]);}
}int main() {int n, m;while(~scanf("%d%d", &m, &n)) {for(int i = 1; i <= n; i++) {scanf("%d", &a[i]);}memset(dp, 0, sizeof(dp));for(int i = 1; i <= n; i++) {for(int j = 0; j <= m; j++) {if(j < a[i]) {dp[i][j] = dp[i-1][j];}else {dp[i][j] = max(dp[i-1][j], dp[i-1][j-a[i]]+a[i]);}}}dfs(n, m);printf("sum:%d\n", dp[n][m]);}return 0;
}

[UVA-562 Dividing coins] 01背包常规

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=503

给定一些硬币,要把这些硬币分给两个人,要求差值最小,输出差值。转化为01背包做,先求出总和,然后对 \(sum/2\) 进行背包。

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;const int maxn = 100*500+5;int dp[maxn];
int a[105];int main() {int t;scanf("%d", &t);while(t--) {int n;int sum = 0;scanf("%d", &n);for(int i = 1; i <= n; i++) {scanf("%d", &a[i]);sum += a[i];}int m = sum / 2; memset(dp, 0, sizeof(dp));for(int i = 1; i <= n; i++) {for(int j = m; j >= a[i]; j--) {dp[j] = max(dp[j], dp[j-a[i]]+a[i]);}}printf("%d\n", abs(sum-2*dp[m]));}return 0;
}

[HDU-2955 Robberies] 01背包常规

http://acm.hdu.edu.cn/showproblem.php?pid=2955

问在“抢劫”被抓的概率不超过给定值的前提下,能总共抢到多少的银行储蓄。

本题需要把被抓的概率先转化成不被抓的概率,这样的独立事件发生概率就可以用乘法来求解。

\(dp[x] = y\) 表示的是 得到 x 的钱、不被抓概率为 y

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;const int maxn = 1e4+5;struct node {int val;double pp;
}a[maxn];double dp[maxn];int main() {int t;scanf("%d", &t);while(t--) {int sum = 0;memset(dp, 0, sizeof(dp));double p;int n;scanf("%lf%d", &p, &n);p = 1.0 - p;for(int i = 1; i <= n; i++) {scanf("%d%lf", &a[i].val, &a[i].pp);a[i].pp = 1.0 - a[i].pp;sum += a[i].val;}dp[0] = 1.0;for(int i = 1; i <= n; i++) {for(int j = sum; j >= a[i].val; j--) {dp[j]= max(dp[j], dp[j-a[i].val]*a[i].pp);}}int ans = 0;for(int i = sum; i >= 0; i--) {if(dp[i] - p >= eps) {ans = i;break;}}printf("%d\n", ans);}return 0;
}

[POJ-2184 Cow Exhibition] 01背包变形

http://poj.org/problem?id=2184

就两个属性总和都不能是负的,然后在这个前提下求二者总和最大。因为有负数的存在,所以我们可以数组多开一倍,然后把负数翻成正数做就行了,二者可以任取一个做为体积。

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;const int maxn = 2e5+5;int a[maxn], b[maxn];
int dp[maxn];int main() {int n;while(~scanf("%d", &n)) {for(int i = 1; i <= n; i++) {scanf("%d%d", &a[i], &b[i]);}for(int i = 0; i < maxn; i++) {dp[i] = -inf;}dp[100000] = 0;for(int i = 1; i <= n; i++) {if(a[i] < 0 && b[i] < 0) {continue;}if(a[i] > 0) {for(int j = 200000; j >= a[i]; j--) {dp[j] = max(dp[j], dp[j-a[i]]+b[i]);}}else {for(int j = a[i]; j <= 200000+a[i]; j++) {dp[j] = max(dp[j], dp[j-a[i]]+b[i]);}}}int ans = -inf;for(int i = 100000; i <= 200000; i++) {if(dp[i] >= 0) {ans = max(ans, dp[i]+i-100000);}}printf("%d\n", ans);}return 0;
}

[HDU-2639 Bone Collector II] 第K大01背包

http://acm.hdu.edu.cn/showproblem.php?pid=2639

求价值第K大的01背包问题,技巧是多加一维表示第k大时的价值,转移之前需要利用归并排序把所有可能情况进行排序。

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;const int maxn = 1e3+5;int val[maxn], cast[maxn];
int dp[maxn][35];
int ta[maxn], tb[maxn];int main() {int n, m, k;int t;scanf("%d", &t);while(t--) {scanf("%d%d%d", &n, &m, &k);for(int i = 1; i <= n; i++) {scanf("%d", &val[i]);}for(int i = 1; i <= n; i++) {scanf("%d", &cast[i]);}memset(dp, 0, sizeof(dp));for(int i = 1; i <= n; i++) {for(int j = m; j >= cast[i]; j--) {for(int l = 1; l <= k; l++) {ta[l] = dp[j][l];tb[l] = dp[j-cast[i]][l] + val[i];}ta[k+1] = tb[k+1] = -1;int x, y, z;x = y = z = 1;while((ta[x] != -1 || tb[y] != -1) && z <= k) {if(ta[x] > tb[y]) {dp[j][z] = ta[x++];}else {dp[j][z] = tb[y++];}if(dp[j][z] != dp[j][z-1]) {z++;}}}}printf("%d\n", dp[m][k]);}return 0;
}

[POJ-2923 Relocation] 01背包 + 状态压缩

http://poj.org/problem?id=2923

这就涉及到我的知识盲区了。

用到状态压缩思想的01背包。先枚举选若干个时的状态,总状态量为1<<n,判断集合里的物品能否一次运走,如果能运走,那就把整个状态看成一个物品。预处理完找到 cnt 个物品,再对这 cnt 个物品进行01背包处理,每个物品的体积是state[i],价值是1,求必选n个物品的最少价值。状态转移方程:\[dp[j|k] = min(dp[j|k], dp[k]+1) 【k为st[i,1\leq j\leq (1<<n)-1]】\]

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;const int maxn = 2e3+5;int a[15];
int dp[maxn];
int st[maxn];
int vis[maxn];
int cnt;
int n, v1, v2;int check(int x) {memset(vis, 0, sizeof(vis));vis[0] = 1;int sum = 0;for(int i = 0; i < n; i++) {if(x & (1<<i)) {// 判断从右到左第 i 位是不是 1(是否被取)sum += a[i];for(int j = v1; j >= a[i]; j--) {if(vis[j-a[i]]) {   // 上一个状态是否存在vis[j] = 1;}}}}if(sum > v1+v2) {return 0;}for(int i = 0; i <= v1; i++) {if(vis[i] && sum-i <= v2) {return 1;}}return 0;
}void init() {cnt = 0;memset(dp, inf, sizeof(dp));for(int i = 0; i < (1<<n); i++) {if(check(i)) {st[cnt++] = i;}}
}int main() {int t, cas = 1;scanf("%d", &t);while(t--) {scanf("%d%d%d", &n, &v1, &v2);for(int i = 0; i < n; i++) {scanf("%d", &a[i]);}init();int m = (1<<n)-1;       // 全部取走的状态dp[0] = 0;      // 取 0 个物品需要 0 次for(int i = 0; i < cnt; i++) {for(int j = m; j >= 0; j--) {if(dp[j] == inf)continue;dp[j|st[i]] = min(dp[j|st[i]], dp[j]+1);}}printf("Scenario #%d:\n", cas++);printf("%d\n\n", dp[m]);}   return 0;
}

[HDU-3466 Proud Merchants] 01背包思维

http://acm.hdu.edu.cn/showproblem.php?pid=3466

题意是有m块钱,要买价值总和最高的几个东西,然后商家看你前不够 qi ,就直接不卖给你了,每个东西花费是 pi。问最多价值

需要先排序的01背包,排序依据是 q-p。

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;const int maxn = 1e4+5;struct node{int p, q, v;bool operator < (const node &x) const {return (q-p) < (x.q-x.p);}
}a[maxn];int dp[maxn];int main() {int n, m;while(~scanf("%d%d", &n, &m)) {for(int i = 1; i <= n; i++) {scanf("%d%d%d", &a[i].p, &a[i].q, &a[i].v);}sort(a+1, a+1+n);memset(dp, 0, sizeof(dp));for(int i = 1; i <= n; i++) {for(int j = m; j >= a[i].q; j--) {dp[j] = max(dp[j], dp[j-a[i].p]+a[i].v);}}printf("%d\n", dp[m]);}return 0;
}

[HDU-2126 Buy the souvenirs] 01背包思维

http://acm.hdu.edu.cn/showproblem.php?pid=2126

问在买到最多件物品的情况下,能有多少种选择方法。

这种情况需要加一维。

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;const int maxn = 5e2+5;int dp[maxn][35];
int a[35];int main() {int t;scanf("%d", &t);while(t--) {int n, m;scanf("%d%d", &n, &m);for(int i = 1; i <= n; i++) {scanf("%d", &a[i]);}memset(dp, 0, sizeof(dp));dp[0][0] = 1;int temp = 0;for(int i = 1; i <= n; i++) {for(int j = m; j >= a[i]; j--) {for(int k = n; k >= 1; k--) {dp[j][k] = dp[j][k] + dp[j-a[i]][k-1];if(dp[j-a[i]][k-1] > 0) {temp = max(temp, k);}}}}if(temp == 0) {printf("Sorry, you can't buy anything.\n");}else {int ans = 0;for(int i = 0; i <= m; i++) {ans += dp[i][temp];}printf("You have %d selection(s) to buy with %d kind(s) of souvenirs.\n", ans, temp);}}return 0;
}

[HDU-4281 Judges' response] 01背包 + 状态压缩 + mTSP

http://acm.hdu.edu.cn/showproblem.php?pid=4281

待填坑... mTSP我不会就是了

转载于:https://www.cnblogs.com/Decray/p/11178231.html

暑假N天乐 —— 01背包及变形相关推荐

  1. 01背包的变形问题----背包恰好装满

    01背包的变形问题----背包恰好装满 在看本文之前建议先看一下我之前发过的01背包详解. https://blog.csdn.net/Iseno_V/article/details/10000113 ...

  2. 饭卡问题(0-1背包的变形)

    电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额.如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够).所以大家 ...

  3. HDU 2546 饭卡 01背包变形

    饭卡 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submiss ...

  4. Cow Frisbee Team S(变相的01背包)

    题目描述 老唐最近迷上了飞盘,约翰想和他一起玩,于是打算从他家的 NN 头奶牛中选出一支队伍. 每只奶牛的能力为整数,第 i 头奶牛的能力为R_i .飞盘队的队员数量不能少于 1.大于N.一支队伍的总 ...

  5. 暑假N天乐 —— 多重+分组背包及变形

    [HDU-1114 Piggy-Bank] 完全背包裸题 http://acm.hdu.edu.cn/showproblem.php?pid=1114 一道迷路的完全背包跑到了这里来...相当于给定背 ...

  6. codeforce Gym 101102A Coins (01背包变形)

    01背包变形,注意dp过程的时候就需要取膜,否则会出错. 代码如下: #include<iostream> #include<cstdio> #include<cstri ...

  7. P1734 最大约数和 01背包变形

    传送门 思路:01背包变形题.将i看成重量,i的因子看成价值即可.背包自然是s. /** * From: * Qingdao Agricultural University * Created by ...

  8. 2022-9-2何以包邮(01背包变形)(c/c++实测满分)

    总结:         此题是背包问题的变形,物品的价值和重量有所改变,背包的容量限制有所改变,但核心动态规划求法没有改变.只需要在背包问题的解法上根据题意对物品表示,答案输出进行改变即可. 背包算法 ...

  9. HDU 3466 01背包变形

    给出物品数量N和总钱数M 对于N个物品.每一个物品有其花费p[i], 特殊值q[i],价值v[i] q[i] 表示当手中剩余的钱数大于q[i]时,才干够买这个物品 首先对N个物品进行 q-p的排序,表 ...

最新文章

  1. html5 图形化操作,HTML5的图像及动画图形操作-20210525222751.ppt-原创力文档
  2. 专访iOS开发框架BeeFramework作者郭虹宇
  3. 玻璃质感_素描丨零基础,你也可以画出玻璃质感
  4. Eclipse新建web项目正常启动tomcat不报错,但不能访问项目的解决方法
  5. 小技巧!CSS 整块文本溢出省略特性探究
  6. 监督学习 | ID3 C4.5 决策树原理
  7. 办公室族的十大饮食危机
  8. 特殊人物请遵循公司工作的基本准则
  9. java载入器材_JAVA之了解类载入器Classloader
  10. 微信小程序图片显示以及动态数据修改(五六)
  11. 史上最全的Linux常用命令汇总(超全面!超详细!)收藏这一篇就够了!
  12. 英语中常用的时态有哪几种?
  13. Appium相对坐标定位元素
  14. 设计模式----仲裁者模式(Mediator Pattern)例题
  15. 数字化时代已经改变了我们的沟通方式
  16. C语言中的rewind()函数
  17. 常用网站攻击手段及防御方法
  18. 快速入门开发实现订单类图片识别结果抽象解析
  19. 【Jsp】第六课 Jsp简介和初步使用
  20. 机器学习中踩过的坑,如何让你变得更专业?

热门文章

  1. 服务器临时文件无法删掉,“internet临时文件”始终删不掉如何解决?临时文件无法删除的清理方法...
  2. 中文句法分析及LTP使用
  3. NCE loss详解
  4. 定制属于你的个性化域名邮箱
  5. 【计算机网络】面试复习
  6. Cannot load driver class: com.mysql.cj.jdbc.Driver 错误解决
  7. 高中生写LOL外挂1年狂赚500万,落网前刚买下120万保时捷...
  8. Microsoft Project(微软Office软件高级版本组件)
  9. cogs 1075. [省常中2011S4] 最短路径问题
  10. 北京市轨道交通车站预约出行实践与思考