大家好,我是wangmy。
众所周知动态规划将原问题分解成若干个子问题,再把子问题分解成若干更多子问题。先求解子问题,再从这些子问题的解得到原问题的解。
今天我给大家分享一下动态规划的几道题和参考代码。
光光的作业
题目描述(Description)
光光上了高中,科目增多了。在长假里,光光的老师们都非常严厉,都给他布置了一定量的作业。 假期里,光光一共有的时间是k小时。在长假前,老师们一共给光光布置了n份作业,第i份作业需 要的时间是ti小时。但是由于老师们互相不商量,因此光光有可能不能完成老师的作业。当不能完成老师的作业时,光光就事后去向老师说明,然后被老师批评一顿了事。对于一件作业,只有2种情况:完成或者不完成(快要完成也算不完成)。如果没完成,受到批评是天经地义的。但是,不同的作业对于光光来说,批评的力度是不同的。第i件作业如果没完成,就要受到pi个单位的批评。多次这样之后,光光想要在长假前就知道他至少会受到多少个单位的批评。你能帮助他吗?
输入格式(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;
}
今天的分享到此结束,我们下次再见。(希望大家有自己的思考,不要一味着抄代码)