Schrödinger’s Knapsack
Time Limit: 1000 ms Memory Limit: 65536 KB
链接
zoj 4019
题目描述
有两类物品,价值分别为k1,k2,数量分别为n,m,给出每个物品占用的体积,每件物品放入背包的价值是放入背包后,k * 当前剩余体积。求这个背包所能装入的最大价值。
Input
包含T组数据。
第一行给出k1,k2和背包容量c。
第二行给出n,m 两种物品数量。
接下来两行分别是物品1和物品2的体积大小。
Sample Input
3
3 2 7
2 3
4 3
1 3 2
1 2 10
3 4
2 1 2
3 2 3 1
1 2 5
1 1
2
1
Sample Output
23
45
10
Solution
线性dp.
当一件物品选择放或者不放,得到的价值与当前背包剩余容量和自己所属哪一类有关,必须有两维i,j记录两类物品选到第几个了,还有一维记录体积。
之后考虑贪心,如果想要得到的价值尽可能大,必然先把体积更小的物品装入背包,使得剩余体积最大化,得到的乘积也就大,所以要装一个物品,比它小的必须全部装完,我们可以优化掉体积这一维,用前缀和来算出选到当前状态的剩余体积。
令dp[i][j]表示第一类选了前i个,第二类选了前j个所获得的最大价值。
dp[i][j] = max(dp[i-1][j] + k1 * 剩余体积 ,dp[i][j-1] + k2 * 剩余体积)
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
const int SZ = 2000 + 10;
ll dp[SZ][SZ],a[SZ],b[SZ];
ll suma[SZ],sumb[SZ];
ll k1,k2,c,n,m,ans;
int main()
{
int T;
scanf("%d",&T);
while(T -- )
{
scanf("%lld%lld%lld%lld%lld",&k1,&k2,&c,&n,&m);
for(int i = 1;i <= n;i ++ ) scanf("%lld",&a[i]);
for(int i = 1;i <= m;i ++ ) scanf("%lld",&b[i]);
sort(a + 1,a + n + 1);
sort(b + 1,b + m + 1);
for(int i = 1;i <= n;i ++ )
{
suma[i] = suma[i - 1] + a[i];
}
for(int i = 1;i <= m;i ++ )
{
sumb[i] = sumb[i - 1] + b[i];
}
dp[0][0] = 0;
ans = 0;
for(int i = 1;i <= n;i ++ )
{
dp[i][0] = dp[i - 1][0] + k1 * (c - suma[i]);
ans = max(dp[i][0],ans);
}
for(int i = 1;i <= m;i ++ )
{
dp[0][i] = dp[0][i - 1] + k2 * (c - sumb[i]);
ans = max(dp[0][i],ans);
}
for(int i = 1;i <= n;i ++ )
for(int j = 1;j <= m;j ++ )
{
dp[i][j] = max(dp[i - 1][j] + k1 * (c - suma[i] - sumb[j]),dp[i][j - 1] + k2 * (c - suma[i] - sumb[j])) ;
ans = max(dp[i][j],ans);
}
printf("%lld\n",ans);
}
return 0;
}
2020.3.23