目录
目录
何为DP
按照网络上的话来说,是这样的:
> 就是把多级最优化问题分解成一系列的单阶问题。在不断增加的过程中,不断的计算当前问题的最优解
>
*其实就是将一个较大的问题,进行剖析,发现其中的关系,列成几个式子,再用编程的语言书写下来*
这就需要我们反复读题,而不是一味地去打代码,也许有的人会说:“光想不打,永远也没有思路。”但往往将读入输出弄完后就只剩下删代码了(我没有思路,换个题做吧)。
总而言之,做DP题,没有草稿纸和笔是不行的。
采药
### 回归原题,状态转移式
f[i][j]:第 i 个人假如只剩 j 的背包容量了,他所能获得的最大价值
而对于每一个物品,它只给你两个选择:
1. 买下这个物品
2. 再想想,不买这一件物品
状态定义
所以一些大佬就想出了这个:所以一些大佬就想出了这个:
**f[i][j] = max(f[i - 1][j],f[i - 1][j - v[i]] + c[i]);**
~~很简单吧~~
代码
#include<cstdio>
#define L 100 + 5
#define M 1000 + 5
inline int max(int x,int y){
return x < y ? y : x;
}
int V,n;
int c[L],v[L],f[L][M];
int main()
{
scanf("%d%d", &V, &n);
for (int i = 1;i <= n;i ++ )
scanf("%d%d", &v[i], &c[i]);
for (int i = 1;i <= n;i ++ )
for (int j = V;j >= 0; j-- ){
if (j >= v[i])
f[i][j] = max(f[i - 1][j],f[i - 1][j - v[i]] + c[i]);//j是指所剩的容量
else
f[i][j] = f[i - 1][j];//这一步不能少,否则当以后的计算只剩下j的容量时,最大价值会出错(0)
}
printf("%d",f[n][V]);
return 0;
}
然后你会猛然发现 i 这东西可要可不要,一个浪费空间的附赠品
变一下
就这样啦
#include<cstdio>
#define L 100 + 5
#define M 1000 + 5
inline int max(int x,int y){
return x < y ? y : x;
}
int V,n;
int c[L],v[L],f[M];
int main()
{
scanf("%d%d", &V, &n);
for (int i = 1;i <= n;i ++ )
scanf("%d%d", &v[i], &c[i]);
for (int i = 1;i <= n;i ++ )
for (int j = V;j >= v[i]; j-- )
f[j] = max(f[j],f[j - v[i]] + c[i]);//j是指所剩的容量
printf("%d",f[V]);
return 0;
}
简单了许多吧,要是你想让你的代码高级一些,又想少费点空间,滚动数组也不失为一个好东西
#include<cstdio>
#define L 100 + 5
#define M 1000 + 5
#define N 2
inline int max(int x,int y){
return x < y ? y : x;
}
int V,n,k = 1;
int c[L],v[L],f[N][M];//千万别忘了给k赋初值
int main()
{
scanf("%d%d", &V, &n);
for (int i = 1;i <= n;i ++ )
scanf("%d%d", &v[i], &c[i]);
for (int i = 1;i <= n;i ++ ){
for (int j = V;j >= v[i]; j-- )
f[k][j] = max(f[1 - k][j],f[1 - k][j - v[i]] + c[i]);//j是指所剩的容量
for (int j = v[i] - 1;j >= 0;j -- )
f[k][j] = f[1 - k][j];
k = 1 - k;
}
printf("%d",f[1-k][V]);
return 0;
}
/*其实,明白了一维做法,k也就很好理解了
通过你的一双慧眼,你会发现:原本的二维方法中,在第i个数时,仅仅只用了i-1中得值
然后惊奇的你,明白了:k的作用只是表示了i,1 - k表示了i - 1
而最后的输出是由于在20行那的k = 1 - k让,存结果的数组成了结果位置的上一个元素*/
然后:
出现了第二题
双塔问题
首先,按照上文所说,分析题目(貌似并没有思路)
再读一遍题,你会看到,两座塔会有那么一丝丝关联(能否使两座塔有同样的高度)
这就是解题de关键所在,让我们尝试写出状态式吧
状态定义
首先我们定义一个 f[i][j] ,i 表示第 i 号元素,j 表示两座塔的高度差(也许很奇怪,这里是假设的两座塔)
接着继续看,很显然,这是一道 01 背包题,必然有放与不放两种状态,接着继续研究放的种情况(既然没有放,肯定没有什么好研究的呀)
1.万一这块水晶放到较高塔上,就是这样(如图)
则表示高塔的原始高度就是 j - a[i](因为较大塔是加过第 i 号塔的高度的)
则就有这样的式子:dp[i][j] = max(dp[i - 1][j - a[i]] + a[i]);
注意这里有一个易错点 j >= a[i]
2.假如放在较低塔,且放了之后仍然较低,如图
则有:dp[i][j] = max(dp[i - 1][j + a[i]]);
3. 假如放在较低塔,变高了,如图
则有:dp[i][j] = max(dp[i - 1][a[i] - j] + j);
a[i] - j 为原本小塔的高度(一个二维数组,可以保存三种值,他的值也可以表示一种值呢)
注意,这里又出现了减法,就一定有 a[i] >= j (千万不能忘)
代码(2)
/*
1.将第i块水晶放到较高的塔上 (j >= a[i])
dp[i][j] = dp[i - 1][j -a[i]] + a[i];
2.将这块水晶放到较低的塔上后仍然较低
dp[i][j] = dp[i - 1][j + a[i]];
3.将这块水晶放到较低的踏上后较高(a[i] >= j)
dp[i][j] = dp[i - 1][a[i] - j] + j;
4.不放
*/
#include<cstdio>
#include<cstring>
#define L 100 + 5
#define M 2000 + 5
#define INT_MIN -2005
int max(int x,int y){
return x < y ? y : x;
}
int n;
int a[L],dp[L][M],b;
int main()
{
scanf("%d", & n);
for (int i = 1;i <= n;i ++ ){
scanf("%d", & a[i]);
b += a[i];//b表示两塔之间的最大差距,即一塔为0,全部水晶都放在一座塔上
}
memset(dp,-0x3f,sizeof(dp));
dp[0][0] = 0;
for (int i = 1;i <= n;i ++ ){
for (int j = b;j >= 0;j -- ){
dp[i][j] = max(dp[i - 1][j],dp[i - 1][j + a[i]]);//比较不放第 i 块水晶与将第 i 快水晶放在较低踏上仍然低
if (j >= a[i])
dp[i][j] = max(dp[i][j],dp[i - 1][j - a[i]] + a[i]);
if (j <= a[i])
dp[i][j] = max(dp[i][j],dp[i - 1][a[i] - j] + j);
}
}
if (dp[n][0] <= 0)//dp[n][0]表示放了 n 块水晶后,两塔相差为0的塔的高度的最大值,因为初始值为极小值,所以dp[n][0]到不了的话,就一定小于0
printf("Impossible\n");
else
printf("%d\n",dp[n][0]);
return 0;
}