大家好,我是wangmy。

众所周知动态规划将原问题分解成若干个子问题,再把子问题分解成若干更多子问题。先求解子问题,再从这些子问题的解得到原问题的解。

今天我给大家分享一下动态规划的几道题和参考代码。

光光的作业

题目描述(Description)

光光上了高中,科目增多了。在长假里,光光的老师们都非常严厉,都给他布置了一定量的作业。 假期里,光光一共有的时间是k小时。在长假前,老师们一共给光光布置了n份作业,第i份作业需 要的时间是ti小时。但是由于老师们互相不商量,因此光光有可能不能完成老师的作业。当不能完成老师的作业时,光光就事后去向老师说明,然后被老师批评一顿了事。对于一件作业,只有2种情况:完成或者不完成(快要完成也算不完成)。如果没完成,受到批评是天经地义的。但是,不同的作业对于光光来说,批评的力度是不同的。第i件作业如果没完成,就要受到pi个单位的批评。多次这样之后,光光想要在长假前就知道他至少会受到多少个单位的批评。你能帮助他吗?动态规划 Part III(上)_c++


输入格式(Format Input)

输入包含以下内容:第一行只有一个数字k,表示光光一共有的时间数
第二行只有一个数字n,表示作业数;接下来n行,每行两个数字,分别是ti和pi,两个数字之间用一个空格分开。

输出格式(Format Output)

输出只包含一行,该行只有一个数字,代表了光光最少受到的批评。

输入样例(Sample Input)

5
3
2 6
1 3
4 7

输出样例(Sample Output)

6

限制(Restrictions)

时间限制(Time Limit):  1000 ms
内存限制(Memory Limit):  65536KB

说明/提示(Hint)

100%的数据中,k≤100000,ti≤10000,pi≤10000
30%的数据中,n≤20;
100%的数据中,n≤500;

参考代码(1)

#include<bits/stdc++.h>
using namespace std;
int t[510], p[510], dp[100010], sum;
//10
//3
//2 6
//1 3
//4 7
//注意区别允不允许从空的状态上转移 
//dp[i][j] 到第i件作业, 花费j时间, 收到的最小批评 
int main()
{
	int k, n;
	cin >> k >> n;
	for(int i = 1; i <= n; i++) 
		scanf("%d%d", &t[i], &p[i]), sum += p[i];
	
	//赋值极大,不影响后续的转移 
	for(int j = 0; j <= k; j++) dp[j] = 1e9;
	dp[0] = sum;

	for(int i = 1; i <= n; i++){
		for(int j = k; j >= t[i]; j--)
			if(dp[j - t[i]] != 1e9)
				dp[j] = min(dp[j], dp[j - t[i]] - p[i]);
	}
	int ans = 1e9;
	for(int j = k; j >= 0; j--)
		ans = min(ans, dp[j]);
	cout << ans;
	
	return 0;
}

参考代码(2)

#include<bits/stdc++.h>
using namespace std;
//在k个时间内,最多能免除多少批评
//dp[i][j], 前i个作业,共花了j个时间,免除最大的批评
//大状态依赖小状态更新,从后往前枚举,保证大状态依赖的小状态是未被更新的(等价于上一个维度) 
//注意区别允不允许从空的状态上转移 
int k,n,t[510], p[510], dp[100010], sum;
int main()
{
	cin >> k >> n;
	for(int i = 1; i <= n; i++) 
		scanf("%d%d", &t[i], &p[i]), sum += p[i];
		dp[0]=0;
	for(int i = 1; i <= n; i++)
	{
		for(int j = k; j >= t[i]; j--)
		//dp[j]=dp[j];//dp[j]自带继承 
			if(dp[j - t[i]]+p[i]>dp[j])
				dp[j]=dp[j-t[i]]+p[i];
	}
	cout<<sum-dp[k];
	return 0;
}

挤牛奶

题目描述(Description)

小卡卡终于帮农夫John找到了走丢的那一头奶牛,John为了感谢小卡卡,不仅告诉了他在 Pascal山脉上可能存在Pascal圣地最大的宝藏,还说要请小卡卡喝牛奶。可是农夫John发现他家里所储藏的牛奶已经喝光了,所以要临时给奶牛挤奶。小卡卡实在是太好心了,说要帮农夫John一起挤牛奶。John答应了,他把他的n头奶牛中的n/2头(n是个偶数)分给小卡卡挤,他自己挤剩下的n/2头奶牛。但是每一头奶牛都有不同的产奶量,农夫John为了让小卡卡减轻负担,他希望他们两个所挤的牛奶的总量之差最小。小卡卡得知了每头奶牛的产奶情况之后很快就解决了这个问题。


输入格式(Format Input)

第一行一个整数n,n为偶数且小于100。表示有n头奶牛。
第二行n个整数分别给出每头奶牛的产奶量(产奶量的总和不超过2000)。

输出格式(Format Output)

输出小卡卡和农夫所挤的牛奶量的最小差值。

输入样例(Sample Input)

5
3
2 6
1 3
4 7

输出样例(Sample Output)

6

限制(Restrictions)

时间限制(Time Limit):  1000 ms
内存限制(Memory Limit):  65536KB

说明/提示(Hint)


参考代码

#include<bits/stdc++.h>
using namespace std;
//dp[i][j][k] 前i头奶牛,选了k头,能否组成k的重量
//dp[i][j][k]=dp[i-1][j][k]||[i-1][j-1][k-a[i]]
//压维,压掉空间,时间不变 
int n, sum, a[110]; 
bool dp[110][2010];
int main()
{
	cin >> n;
	for(int i = 1; i <= n; i++)
		scanf("%d", &a[i]), sum += a[i];
	dp[0][0] = 1;
	 
	for(int i = 1; i <= n; i++){ //枚举牛到了那一头
		for(int j = i; j >= 1; j--){ //一共选取了多少头牛
			for(int k = sum; k >= a[i]; k--){//产奶量
				if(dp[j - 1][k - a[i]]) 
					dp[j][k] = dp[j - 1][k - a[i]];
			}
		}
	}
	for(int i = sum / 2; i >= 0; i--){
		if(dp[n / 2][i]){
			cout << sum - i * 2;
			break;
		}
	}
	return 0;
}

打包

题目描述(Description)

你现在拿到了许多的礼物,你要把这些礼物放进袋子里。你只有一个最多装下V 体积物品的袋子,你不能全部放进去。你也拿不动那么重的东西。你估计你能拿的最大重量为 G。现在你了解了每一个物品的完美值、重量和体积,你当然想让袋子中装的物品的完美值总和最大,你又得计划一下了。


输入格式(Format Input)

第一行:G 和 V 表示最大重量和体积。 
第二行:N 表示拿到 N 件礼物。
第三到N+2行:每行3个数 Ti Gi Vi 表示各礼物的完美值、重量和体积

输出格式(Format Output)

输出共一个数,表示可能获得的最大完美值。

输入样例(Sample Input)

6 5		
4		
10 2 2		
20 3 2
40 4 3
30 3 3

输出样例(Sample Output)

50

限制(Restrictions)

时间限制(Time Limit):  1000 ms
内存限制(Memory Limit):  65536KB

说明/提示(Hint)

对于20%的数据 N,V,G,Ti,Vi,Gi≤10
对于50%的数据 N,V,G,Ti,Vi,Gi≤100
对于80%的数据 N,V,G,Ti,Vi,Gi≤300
80%到100%的数据是N,V,G,Ti,Vi,Gi≤380 的离散随机数据。

参考代码

#include<bits/stdc++.h>
using namespace std;
int g, v, n, T[385], G[385], V[385];
int dp[385][385];
int main()
{
	//    G 和 V 表示最大重量和体积
	cin >> g >> v >> n;
	for(int i = 1; i <= n; i++) 
		//完美值、重量和体积 
		scanf("%d %d %d", &T[i], &G[i], &V[i]);
	
	
	for(int i = 1; i <= n; i++){
		for(int j = g; j >= G[i]; j--)
			for(int k = v; k >= V[i]; k--)
				if(dp[j][k] < dp[j - G[i]][k - V[i]] + T[i])
					dp[j][k] = dp[j - G[i]][k - V[i]] + T[i];
//		for(int j = g - G[i]; j >= 0; j--)
//			for(int k = v - V[i]; k >= 0; k--)
//				if(dp[j + G[i]][k + V[i]] < dp[j][k] + T[i])
//					dp[j + G[i]][k + V[i]] = dp[j][k] + T[i];
	}
	cout << dp[g][v];
	
	return 0;
}

今天的分享到此结束,我们下次再见。(希望大家有自己的思考,不要一味着抄代码)