题 意:小明家着火了,有n的物品需要救援,给出,每个物品的抢救时间t,彻底被烧毁的时间d,和价值p。如果小明先抢救物品a,再抢救物品b,那么只有当ta+tb
3
3 7 4
2 6 5
3 7 6
输出样例:
11
2
2 3
思 路:选或不选的最优决策,显然是一道0,1背包。关键点是什么呢?
关键点1:按时间排序,显然时间最少的应该放在前面,决则这样可以使背包跑出来的结果最优,因为我可以先拿时间少的,然后再去拿时间大的,而可能不能先拿时间大,再拿时间少的。不排序,跑出来的结果,不是最优的。
关键点2:状态转移方程dp[i][j] 到第i个时间点j时所救援的最大物品价值.
dp[i][j] = max(dp[i-1][j],d[i-1][j-ti]+pi)
初始状态dp[0][j] = 0
关键点3:路径输出 用一个last[i][j] 数组 记录 到第i个时间点j时 是否选择了i 然后再从dp[n][j]最大价值中,回溯就好。
#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 100+5;
struct node{
int t,d,p,id;
}a[maxn];
int dp[maxn][2001]; //前i个到时间j所能获取的最大价值
/*
状态转移方程 dp[i][j] = max(dp[i-1][j],dp[i-1][j-ti]+pi);
*/
bool cmp(node a, node b){
return a.d < b.d;
} //这样可以保证再前面选的经可最大
int last[maxn][2000+1];
int n;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d %d %d",&a[i].t,&a[i].d,&a[i].p);
a[i].id = i;
}
sort(a+1,a+n+1,cmp);
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++){
for(int j=1;j<=2000;j++){
dp[i][j] = dp[i-1][j];
if(j >= a[i].t && j<a[i].d){
if(dp[i-1][j-a[i].t]+a[i].p > dp[i][j]){
dp[i][j] = dp[i-1][j-a[i].t]+a[i].p;
last[i][j] = i;
}
}
}
}
vector<int> vec;
int ans = 0,index;
for(int i=1;i<=2000;i++){
if(dp[n][i] > ans){
ans = dp[n][i];
index = i;
}
}
vec.clear();
int level = n,pos = index;
while(level >= 1 && pos >=0){
if(last[level][pos] > 0){
vec.push_back(a[last[level][pos]].id);
pos-=a[last[level][pos]].t;
}
level--;
}
reverse(vec.begin(),vec.end());
printf("%d\n",ans);
printf("%d\n",vec.size());
for(int i=0;i<vec.size();i++){
printf("%d%c",vec[i],i==n-1?'\n':' ');
}
return 0;
}