Codeforce 864E 需要优先处理的01背包

该博客讨论了一种动态规划策略,用于解决在火灾中拯救物品以最大化价值的问题。文章详细解释了如何根据物品的拯救时间和消失时间,以及它们的价值来制定决策。通过倒序枚举时间并排序物品,确保了在有限时间内尽可能拯救高价值的物品。代码示例展示了如何实现这一策略,并给出了最优解决方案的输出。
题意:

一个房子发生了火灾,给出nnn个物品,每一行依次是ti,di,pit_{i},d_{i},p_{i}ti,di,pi,分别代表拯救这个物品需要的时间,物品消失的时间(意味着完成拯救这个物品的时间timetimetime必须要time≤ditime\leq d_{i}timedi),这个物品的价值。求出能拯救的物品组合的最大价值,并给出拯救顺序,一次只能拯救一件物品。

思路:

时间的流逝是适合dpdpdp的,因为决策可以依赖时间转移,每一个时间可以做出一个决策,设f[i]f[i]f[i]为前iii分钟能拯救的物品组合的最大价值,拯救顺序选择用vectorvectorvector存储,当f[i]f[i]f[i]继承时,对应的vectorvectorvector也继承,并且push_backpush\_backpush_back当前拯救的元素。那么如何转移呢?这显然是和010101背包类似的,倒序枚举时间,每个时间可以选择救也可以选择不救,但是显然每个时刻尽量选择救是最好的。所以状态转移方程为f[i]=max(f[i],f[i−t[j]]+p[j])f[i]=max(f[i],f[i-t[j]]+p[j])f[i]=max(f[i],f[it[j]]+p[j]),前提是开始的时间合法,完成拯救的时间也合法。其次,我们需要对物品排序,因为did_{i}di最小的物品应该是最先枚举的,假设有这么个例子,输入数据是

2
3 8 2
3 4 1

我们先枚举第一件物品,使得f[3]=2f[3]=2f[3]=2,在第333秒拯救了第一件物品,第二件物品枚举时无法接在333秒之后,因为第333秒已经消失了。而排序完先枚举第二个物品,f[3]=1f[3]=1f[3]=1,此时第一件物品枚举时,f[6]f[6]f[6]可以有f[3]f[3]f[3]转移来,每次转移是接在之前救完的后面,我们让时间宽裕的尽量在后面拯救,这样才能获得最大值。

代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;

ll read()
{
	ll ret=0,base=1;
	char ch=getchar();
	while(!isdigit(ch))
	{
		if(ch=='-') base=-1;
		ch=getchar();
	}
	while(isdigit(ch))
	{
		ret=(ret<<3)+(ret<<1)+ch-48;
		ch=getchar();
	}
	return ret*base;
}

int n,t[105],d[105],p[105],max1,dp[2005],ans,a[2005];
vector<int>v[2005];

bool cmp(int a,int b)
{
	return d[a]<d[b];
}

int main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		a[i]=i;
		t[i]=read();
		d[i]=read();
		p[i]=read();
		max1=max(max1,d[i]);
	}
	sort(a+1,a+1+n,cmp);
//	for(int i=1;i<=n;i++) printf("%d ",a[i]);
//	cout<<endl;
	for(int i=1;i<=n;i++)
	{
		if(t[a[i]]<d[a[i]])
		{
			for(int j=max1;j>=1;j--)
			{
				if(j<d[a[i]]&&j-t[a[i]]+1>=1&&dp[j-t[a[i]]]+p[a[i]]>dp[j])//要救现在的这个物品
				{
					dp[j]=dp[j-t[a[i]]]+p[a[i]];
					v[j]=v[j-t[a[i]]];
					v[j].push_back(a[i]);
				}
			}
		}
	}
//	for(int i=1;i<=max1;i++) printf("dp[%d]=%d\n",i,dp[i]);
	int pos;
	for(int i=1;i<=max1;i++)
	{
		if(dp[i]>ans)
		{
			ans=dp[i];
			pos=i;	
		}
	}
	cout<<ans<<endl;
	printf("%d\n",v[pos].size());
	for(int i:v[pos]) printf("%d ",i);
 	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值