题意:
一个房子发生了火灾,给出nnn个物品,每一行依次是ti,di,pit_{i},d_{i},p_{i}ti,di,pi,分别代表拯救这个物品需要的时间,物品消失的时间(意味着完成拯救这个物品的时间timetimetime必须要time≤ditime\leq d_{i}time≤di),这个物品的价值。求出能拯救的物品组合的最大价值,并给出拯救顺序,一次只能拯救一件物品。
思路:
时间的流逝是适合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[i−t[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;
}
该博客讨论了一种动态规划策略,用于解决在火灾中拯救物品以最大化价值的问题。文章详细解释了如何根据物品的拯救时间和消失时间,以及它们的价值来制定决策。通过倒序枚举时间并排序物品,确保了在有限时间内尽可能拯救高价值的物品。代码示例展示了如何实现这一策略,并给出了最优解决方案的输出。
1378

被折叠的 条评论
为什么被折叠?



