Zoj-4019 Schrödinger's Knapsack(线性dp)

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值