01背包
二维数组存放
状态转移方程:(c[i][j]表示将前i个物品放入容量为j的背包中最能获得的最大价值)
if(j>=wi)
c[i][j] = max{c[i-1][j], c[i-1][j-w[i]]+v[i]}
else
c[i][j]=c[i-1][j]
第0行初始化为0
i=1~N
j=0~w
if(j>=wi)
c[i][j] = max{c[i-1][j], c[i-1][j-w[i]]+v[i]}
else
c[i][j]=c[i-1][j]
注意到第i行上的数据仅和第i-1行中的数据有关,即第i行上的每个数据是由第i-1行上的两个数据决定的,所以可以仅由一个一维数组来存放结果
初始化c的每个数据为0
i=1~N
j=w~wi
c[j]=max{c[j],c[j-wi]+vi}
注意点:
a.外层循环i代表前i个物品,比如i=1时,整个数组就覆盖了原来的数据,整个数组变成了原来的c[1][j],同理i=2,3,4也是如此
b.在进行每次循环的时候,要逆序进行覆盖,这是因为,01背包本质上还是用二维数组进行计算的,用二维数组计算的话,是一行一行计算的。也就是说,在前1到i-1行都填好的情况下,第i行根据第i-1行的数据进行计算。如果正序进行覆盖,则前面的值有可能发生改变,也就是第i-1行中的数据改变了,而后面的值,有可能用到这已经改变的前面的值,使结果不对。
比如c5可由原来的c5 c2(i-1行)获得,假如c5(i-1)的值改变,在接下来进行c7 c7有可能由c5 c7(i-1行)获得,那这样正序的话结果是不对的。
c.上述的j循环只从W降到wi,wi-1到0没有执行,这是因为不执行就相当于c[i]=ci,对应了二维数组中c[i][j]=c[i-1][j]的情况
网上还提到一个常数的优化,即在上述一维数组的情况下j循环到下限是max(W-sum(i-n)wi,wi),回到二维数组的情况,这是由于我们要求的c[n][w]=f(c[n-1][w],c[n-1][w-wn]),而c[n-1][w-wi]=f(c[n-2][w],c[n-2][w-wn-wn-1]),以此类推,c[1][w]=f(c[0][w],c[0][w-wn-wn-1-wn-2-~w1]),也就是说,对应循环i, 我只要循环下限到w-sum(i~n)wi就行了,所以优化过后的下限是ax(W-sum(i-n)wi,wi)
在这道题中,如果去掉Bi这个分量,那么就是01背包问题,但是有Bi这个分量就要考虑加入背包中的顺序了。不妨设X1X2……XN为“可以”装入背包的一种选择,
考虑其中一对XtXt+1,不管这两个数的前面的数和后面的数怎么变,XtXt+1和XtXt+1这两种摆法不一样的地方只有Bt+1Ct和BtCt+1,可以发现当前面的B/C大时,损失的分数较小。又由于这t是任意的,所以可以推广到全部的t(例如Xt+1Xt+2,不管前面的数和后面的数怎么变,只有保证Xt的B/C值比Xt+1的值大就可以了,推导过程也和上面类似),所以当以B/C的值从大到小排序时,用背包问题求得的解就是“这一种做法”的最大值。换句话说传统的01背包问题从许多种选法a1,a2,a3……at(ai对应可以装入背包的一种物品的选法)种获得最大值,而排序则保证了a1,a2,a3等分别获得最大值,即分别取得它们本身的最大,所以再用01背包问题获得最大值就是全部选法的最大值。
这个题目还有一个问题,即价值要减去装入背包的题目的时间和(代价和),01背包问题只求得了一个最大的值,并没有那么仔细的关注谁装入了背包,所以这又是一个问题。网上很多人说的设dp[i]为恰巧装满时的价值,但是转移方程还是和01背包一样,并不能保证每步都是装满的。而且每步所减去的已装入背包的时间和是dp方程中的j,考虑到有可能没装满,这个值有可能是偏大的。
实际上传统01背包问题还有一个性质,即它的二维数组的每一行都是非递减的
只要j处的值比j-1处的值大,那j处的背包就是装满的(j为1时只要不是0就可以了)就比如这样的
平的部分的点就是没有装满的。这些点都是属于能够装进去的,也就是dp[j-c[j]]
+a[j]这样子的,这样迭代到最后会出现dp[x]这个x很小没有东西能够装进去就是0了,所以都是不变的。而这题中减去的时间和是j,所以不会是平的,而是递减的
类似这样
每次递减的值就是1到i的物品中小于j的时间和,比如5为2+3,可以看到递减的还是个等差数列,所以要求最后到底最大值为多少,就是要求在“装满”的情况下的dp[i][j],由以上,要求的就是最后一行,每个递减的组的头部的那个数据就是装满的,即求最大值。因为后一个装满的不一定比前一个装满的大,比如第4行的48和69,同时最后一个也有可能处于递减中。综上,以下为代码:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
int T;
int n, t;
typedef struct problem
{
int a;
int b;
int c;
}problem;
problem pros[1001];
int dp[3001];
bool cmp(problem a, problem b)
{
if (a.b*b.c> b.b*a.c)
{
return true;
}
return false;
}
int main()
{
scanf("%d", &T);
while (T--)
{
scanf("%d%d", &n, &t);
for (int i = 1; i <= n; i++)
{
scanf("%d%d%d", &pros[i].a, &pros[i].b, &pros[i].c);
}
sort(pros + 1, pros + n+1, cmp);
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i++)
{
for (int j = t; j >= pros[i].c;j--)
{
dp[j] = max(dp[j], dp[j - pros[i].c] + pros[i].a-j*pros[i].b);
}
/*
for (int k = 1; k <= t; k++)
{
printf("%d ", dp[k]);
}
printf("\n");
*/
}
int maxx = -1;
for (int i = 1; i <= t; i++)
{
maxx = max(maxx, dp[i]);
}
printf("%d\n", maxx);
}
return 0;
}