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

ACM:动态规划,01背包问题

背包型动态规划

参赛博文|0-1背包问题(动态规划)附例题详解——java实现

动态规划入门-01背包问题 - poj3624

动态规划Ⅲ