ACM - 动态规划小白入门
Posted 肆呀
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ACM - 动态规划小白入门相关的知识,希望对你有一定的参考价值。
动态规划 - AcWing 基础课
【 本文主要来自对AcWing基础算法课DP(背包 / 线性 / 区间 / 计数 / 数位统计 / 状压 / 树形 / 记忆化 DP)的整理,基本是母题 】
一、背包问题
1、01背包 : 每件物品最多只能选一次
AcWing 2. 01背包问题
原题链接:https://www.acwing.com/problem/content/2/
思路
#include <bits/stdc++.h>
using namespace std;
#define getlen(array) return (sizeof(array) / sizeof(array[0]));
#define ll long long
#define ull unsigned long long
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define MEM(x, y) memset(x, y, sizeof x)
#define rin int n; scanf("%d", &n)
#define rln ll n; scanf("%lld", &n)
#define rim int m; scanf("%d", &m)
#define rit int t; scanf("%d", &t)
#define ria int a; scanf("%d", &a)
#define sc scanf
#define pr printf
const int INF = 0x3f3f3f3f;
const int N = 1010;
int dx[] = -1, 1, 0, 0, dy[] = 0, 0, -1, 1;
int v[N], w[N];
int dp[N]; //优化空间
int main()
//freopen("D:\\\\in.txt", "r", stdin);
rin;
rim;
for (int i = 1; i <= n; ++ i) cin >> v[i] >> w[i];
for (int i = 1; i <= n; ++ i)
for (int j = m; j >= v[i]; -- j)
dp[j] = dp[j];
dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
cout << dp[m];
return 0;
2、完全背包 : 每件物品可以选任意次
AcWing 3. 完全背包问题
原题链接:https://www.acwing.com/problem/content/3/
思路
因为背包的体积不是无限大,所以设每一件物品最多只能放 k 件进背包。
如果 k 较大,可能会TLE,所以还可以再优化一下:
#include <bits/stdc++.h>
using namespace std;
#define getlen(array) return (sizeof(array) / sizeof(array[0]));
#define ll long long
#define ull unsigned long long
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define MEM(x, y) memset(x, y, sizeof x)
#define rin int n; scanf("%d", &n)
#define rln ll n; scanf("%lld", &n)
#define rim int m; scanf("%d", &m)
#define rit int t; scanf("%d", &t)
#define ria int a; scanf("%d", &a)
#define sc scanf
#define pr printf
const int INF = 0x3f3f3f3f;
const int N = 1010;
int dx[] = -1, 1, 0, 0, dy[] = 0, 0, -1, 1;
int v[N], w[N];
int dp[N][N];
int main()
//freopen("D:\\\\in.txt", "r", stdin);
rin;
rim;
for (int i = 1; i <= n; ++ i) cin >> v[i] >> w[i];
for (int i = 1; i <= n; ++ i)
for (int j = 1; j <= m; ++ j)
dp[i][j] = dp[i - 1][j];
if (v[i] <= j) dp[i][j] = max(dp[i][j], dp[i][j - v[i]] + w[i]);
cout << dp[n][m];
return 0;
3、多重背包(朴素版):限定每件物品选择次数的上限
AcWing 4. 多重背包问题 I
原题链接:https://www.acwing.com/problem/content/4/
思路
由于 dp[ i ] [ j - v] 展开后会多出额外的一项(因为有上限 s[ i ]),所以不能和完全背包一样用以前的状态替换掉。
#include <bits/stdc++.h>
using namespace std;
#define getlen(array) return (sizeof(array) / sizeof(array[0]));
#define ll long long
#define ull unsigned long long
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define MEM(x, y) memset(x, y, sizeof x)
#define rin int n; scanf("%d", &n)
#define rln ll n; scanf("%lld", &n)
#define rim int m; scanf("%d", &m)
#define rit int t; scanf("%d", &t)
#define ria int a; scanf("%d", &a)
#define sc scanf
#define pr printf
const int INF = 0x3f3f3f3f;
const int N = 110;
int dx[] = -1, 1, 0, 0, dy[] = 0, 0, -1, 1;
int v[N], w[N], s[N];
int dp[N][N];
int main()
rin;
rim;
for (int i = 1; i <= n; ++ i) cin >> v[i] >> w[i] >> s[i];
for (int i = 1; i <= n; ++ i)
for (int j = 1; j <= m; ++ j)
for (int k = 0; k <= s[i] && k * v[i] <= j; ++ k)
dp[i][j] = max(dp[i][j], dp[i - 1][j - k * v[i]] + k * w[i]);
cout << dp[n][m];
return 0;
4、多重背包(二进制优化版):限定每件物品选择次数的上限,但是上限较大
AcWing 5. 多重背包问题 II
原题链接:https://www.acwing.com/problem/content/5/
思路
首先明确一点,用1、2、4、8、……、64 等数可以拼凑起 0 到 127 之间的任意一个数,假如想要拼凑 0 到 200 之间的任意一个数,那么至少需要 1、2、4、8、……、64、73 这些数。
因为题目中 s [ i ] 的范围较大,不可能再开一重循环,所以就考虑将 s [ i ] 拆开为 1、2、4、……,由此可以保证每一种组合都会被涉及到。
最后合成的全新的 v 和 w 数组,再用一次 01 背包即可求得最终解。
#include <bits/stdc++.h>
using namespace std;
#define getlen(array) return (sizeof(array) / sizeof(array[0]));
#define ll long long
#define ull unsigned long long
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define MEM(x, y) memset(x, y, sizeof x)
#define rin int n; scanf("%d", &n)
#define rln ll n; scanf("%lld", &n)
#define rim int m; scanf("%d", &m)
#define rit int t; scanf("%d", &t)
#define ria int a; scanf("%d", &a)
#define sc scanf
#define pr printf
const int INF = 0x3f3f3f3f;
const int N = 30000;
int dx[] = -1, 1, 0, 0, dy[] = 0, 0, -1, 1;
int v[N], w[N];
int dp[N];
int main()
//freopen("D:\\\\in.txt", "r", stdin);
int n, m;
cin >> n >> m;
int cnt = 1;
for (int i = 0; i < n; ++ i)
int a, b, c;
cin >> a >> b >> c;
int num = 1; // num :1、2、4、8、……
while (num <= c)
v[cnt] = num * a;
w[cnt] = num * b;
++ cnt;
c -= num;
num *= 2;
if (c > 0)
v[cnt] = c * a;
w[cnt] = c * b;
++ cnt;
for (int i = 1; i < cnt; ++ i)
for (int j = m; j >= v[i]; -- j)
dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
cout << dp[m];
return 0;
5、分组背包:分为 x 组,每一组最多只能选择其中的一件
AcWing 9. 分组背包问题
原题链接:https://www.acwing.com/problem/content/9/
思路
#include <bits/stdc++.h>
using namespace std;
#define getlen(array) return (sizeof(array) / sizeof(array[0]));
#define ll long long
#define ull unsigned long long
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define MEM(x, y) memset(x, y, sizeof x)
#define rin int n; scanf("%d", &n)
#define rln ll n; scanf("%lld", &nACM - 动态规划小白入门:背包 / 线性 / 区间 / 计数 / 数位统计 / 状压 / 树形 / 记忆化 DP