题目描述:
题目背景
据说在红雾异变时,博丽灵梦单身前往红魔馆,用十分强硬的手段将事件解决了。
然而当时灵梦在 Power 达到 MAX 之前,不具有“上线收点”的能力,所以她想要知道她能收集多少 P 点,然而这个问题她答不上来,于是她找到了学 OI 的你。
题目描述
可以把游戏界面理解成一个 N 行 M 列的棋盘,有 K 个格子上有 P 点,其价值为 val(i,j)。
初始灵梦可以选择在第一行的任意一个格子出发,每秒她必须下移一格。
灵梦具有一个左右移动的速度 T,可以使她每秒向左或右移动至多 T 格,也可以不移动,并且不能折返。移动可视为瞬间完成,不经过路途上的点,只能获得目标格子的 P 点。
求最终她能获得的 POWER 值最大是多少?
输入格式
第一行四个数字,N,M,K,T。
接下来 K 行每行 3 个数字 x,y,v,代表第 x 行第 y 列有一个 val 为 v 的 P 点,数据保证一个格子上最多只有 1 个 P 点。
输出格式
一个数字
样例输入
3 3 4 1
1 1 3
1 2 1
2 2 3
3 3 3
样例输出
9
题目分析:
先按照每个P点的行从小到大排个序
我们考虑dp[i]表示走到第i个P点时power值最大是多少,能由在他上面的个P点走过来,就可以得出以下状态转移方程:
dp[i]=max(dp[i],dp[j]+val[i]);
那么问题来了,需要符合怎样的要求才能进行转移?
我们设上面的P点坐标为P(x,y),下降了L秒即纵坐标下降了L格,由于最大速度为T格每秒,所以下面的P点一定在P(x-L*T,y+L),P2(x+L*T,y+L)这个直线上。故此,我们可以就此完成dp部分代码
for(int i=1;i<=k;i++){
dp[i]=p[i].v;
for(int j=0;j<i;j++)
if(abs(p[i].y-p[j].y)<=t*(p[i].x-p[j].x))//本题x与y颠倒
dp[i]=max(dp[i],dp[j]+p[i].v);
ans=max(ans,dp[i]);
}
到此这个题基本结束,我们把前面的部分写写,在注意下小细节,这个题就出来了
坑点:
1.注意本题中x是纵坐标,y是横坐标
2.注意初始化,将dp[i]赋值为val[i]
代码:
下面放上本题的AC代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=4005;
int n,m,t,k;
int dp[N];
struct node{
int x,y,v;
}p[N];
bool cmp(node A,node B){
return A.x<B.x;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>k>>t;
for(int i=1;i<=k;i++){
cin>>p[i].x>>p[i].y>>p[i].v;
}
sort(p+1,p+1+k,cmp);
int ans=0;
for(int i=1;i<=k;i++){
dp[i]=p[i].v;
for(int j=0;j<i;j++)
if(abs(p[i].y-p[j].y)<=t*(p[i].x-p[j].x))
dp[i]=max(dp[i],dp[j]+p[i].v);
ans=max(ans,dp[i]);
}
cout<<ans;
return 0;
}
感想:
奔着单调队列优化dp来的,后来发现就是个LIS水题,感觉可以评黄。加我认为这种做法才是正解,单调队列优化dp纯是想复杂了。看完这篇题解,希望您能对dp有更深的理解,带给您的帮助。也鼓励大家在我的评论区里提出更好的做法。渴望得到大家的支持!!