题目
https://www.luogu.com.cn/problem/P3800
思路
显然这是一道DP题,朴素算法很容易想:
[dp[i][j]=v[i][j]+max(dp[i-1][k])(j-tleq kleq j+t)
]
然而这样的转移是(O(nm^2))的,不合要求,还要继续优化。
注意到(kleq 4000),棋盘的数据是相当稀疏的,那么我们考虑将有P点的格子当成一个结点,建图。根据题意(必须一直向下走)该图是一个DAG。
边的构造:将结点按x坐标从小到大排序,可以算出从i结点的行到j结点的行经过的时间(x_j-x_i),那么如果这段时间内能从i结点到j结点,那么建一条从i到j的有向边。
跑一遍记忆化dfs就行了。
易错点:不要以为答案就是dfs(1),因为最优解完全可以从空格子开始,起点是不固定的。
代码
#include<cstdlib>
#include<algorithm>
using namespace std;
struct node{
int x,y,val;
bool operator <(node t){
return x<t.x;
}
} a[4001];
int book[4001][4001],dp[4001],k,vis[4001];
int dfs(int x){
int i;
vis[x]=1;
if(dp[x]) return dp[x];
dp[x]=a[x].val;
for(i=x+1;i<=k;i++){
if(book[x][i]) dp[x]=max(dp[x],a[x].val+dfs(i));
}
return dp[x];
}
int main(){
int i,j,l,n,m,t,x,y,z;
int ans=0;
scanf("%d%d%d%d",&n,&m,&k,&t);
for(i=1;i<=k;i++)
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].val);
sort(a+1,a+k+1);
for(i=1;i<=k;++i){
for(j=i+1;j<=k;++j){
int l=a[i].y-(a[j].x-a[i].x)*t;
int r=a[i].y+(a[j].x-a[i].x)*t;
if(a[j].y>=l&&a[j].y<=r) book[i][j]=1;
}
}
for(i=1;i<=k;++i){
if(!vis[i]) dfs(i);
}
for(i=1;i<=k;i++)
ans=max(ans,dp[i]);
printf("%d",ans);
return 0;
}