题目←
考试的时候沉迷T4而忽略掉的DP题
也是该复习DP了……
摸鱼:
设dp[i][j]为第i个物品在时间为j时的最大高度
转移更直观一些
dp[i][j] = dp[i - 1][j - l[i].t];//吃
dp[i][j] = dp[i - 1][j] + l[i].h//堆
注意在吃的时候,需要满足j - l[i].t >= l[i].f,堆得话需满足j >= l[i].f
意义为在使用了i - 1堆垃圾而到达第i堆垃圾扔下的时间的情况,这样才是合法的
#include
#include
#include
#include
using namespace std;
const int MAXN = 20000 + 50;
struct edge{
int f,t,h;
}l[MAXN << 1];
int D,G,dp[105][MAXN],lim;
bool cmp(edge a,edge b){
return a.f < b.f;
}
int main(){
scanf("%d%d",&D,&G);
for(int i = 1;i <= G;i ++){
scanf("%d%d%d",&l[i].f,&l[i].t,&l[i].h);
}
sort(l + 1,l + G + 1,cmp);
memset(dp,-1,sizeof(dp));
for(int i = 0;i <= 10;i ++){
dp[0][i] = 0;
}
lim = 10;
for(int i = 1;i <= G;i ++){
if(lim < l[i].f){
printf("%d",lim);
return 0;
}
lim += l[i].t;
for(int j = 0;j <= lim;j ++){
dp[i][j] = dp[i - 1][j];
if(j >= l[i].f){
if(j - l[i].t >= l[i].f)dp[i][j] = max(dp[i][j],dp[i - 1][j - l[i].t]);
if(dp[i - 1][j] != -1)
dp[i][j] = max(dp[i][j],dp[i - 1][j] + l[i].h);
if(dp[i][j] >= D){
printf("%d\n",l[i].f);
return 0;
}
}
}
}
/*for(int i = 1;i <= G;i ++){
for(int j = 0;j <= lim;j ++){
printf("%d ",dp[i][j]);
}
printf("\n");
}*/
printf("%d",lim);
return 0;
}
正解:
如果不看题解是没有这种思路的……
充分体现了DP的另一个技巧:设计不出方程的时候把数组下标和它存的内容换一换
设f[j]为到达高度i时的最大生命时间
对于垃圾i,有吃和堆的情况:
f[j + l[i].h] = max(f[j + l[i].h],f[j]);//堆,高度增时间不增
f[j] += l[i].w //吃,吃掉时一定为这一高度的最优情况
关于垃圾到达时间的限制,仅当f[j] >= 垃圾i的到达时间时,才可以选择堆或吃
否则为i之前的垃圾无法在i到来的时刻堆到高度j,该高度不合法。
如果无法到达则全部吃掉可存活最长时间,输出f[0]
确实有点像带限制的01背包?
#include
#include
#include
using namespace std;
int D,n;
struct edge{
int f,h,w;
}l[23333];
int f[233333];
int solve(){
f[0] = 10;
for(int i = 1;i <= n;i ++){
for(int j = D;j >= 0;j --){
if(l[i].f <= f[j]){
f[j + l[i].h] = max(f[j + l[i].h],f[j]);
if(j + l[i].h >= D)return l[i].f;
f[j] += l[i].w;
}
}
}
return -1;
}
bool cmp(edge a,edge b){
return a.f < b.f;
}
int main(){
scanf("%d%d",&D,&n);
for(int i = 1;i <= n;i ++){
scanf("%d%d%d",&l[i].f,&l[i].w,&l[i].h);
}
sort(l + 1,l + n + 1,cmp);
int temp = solve();
if(temp == -1)temp = f[0];
printf("%d",temp);
return 0;
}