动规题。
传送门:GO
首先处理出前缀和。
s[i][j]表示时间为i时,在j点的金币前缀和,这个更新是沿着对角线来的。
假如机器人在第t秒,第j个位置上,下一次一定是(t+1,(j+1)%n)
设f[i][j]表示以上情况下的最大金币量,枚举行走步数,大致口胡得到:
f[i][j]=max(f[i][j],f[(i-k+n)%n][j-k]+s[(i-k+n)%n][k])
但是发现枚举的东西很多(位置,时间,跳跃步长),时间复杂度O(n^3)
所以可以不要第一维,因为p<=m,所以每次前进不会超过一圈,也就是更新某个位置的状态时,不会有后效性。
用f[i]表示时间为i时,能获得的最大金币量。
如果在某个位置j放了机器人,行走步长为k,修理机器人的钱用fix[i]表示,那么状态转移就是:
f[i]=max(f[i-k]-sum[i-k][j-k]-fix[j-k])+sum[i][j]
括号里的一坨用单调队列搞一下,取最大值即可。
(为什么普及组的题会考单调队列,wsl)
膜拜一下这位大大的题解:GO
花了好久才看懂,但确实受益很多了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int read(){ 4 int f=1,x=0; 5 char c=getchar(); 6 while(!isdigit(c)){ 7 if(c=='-') f=-1; 8 c=getchar(); 9 } 10 while(isdigit(c)){ 11 x=(x<<1)+(x<<3)+(c^48); 12 c=getchar(); 13 } 14 return x*f; 15 } 16 const int N=1010; 17 int n,m,p; 18 int a[N][N]; 19 int sum[N][N]; 20 int fix[N]; 21 int f[N]; 22 struct node{ 23 int val,idx; 24 }; 25 deque<node> t[N]; 26 int main(){ 27 n=read();m=read();p=read(); 28 for(int i=1;i<=n;i++){ 29 for(int j=1;j<=m;j++){ 30 a[j][i]=read(); 31 } 32 } 33 int j_=1; 34 for(int i=1;i<=m;i++){ 35 for(int j=1;j<=n;j++){ 36 if(j>1) j_=(j_==n?1:j_+1); 37 sum[i][j_]=sum[i-1][j_]+a[i][j]; 38 } 39 } 40 for(int i=1;i<=n;i++){ 41 fix[i]=read(); 42 } 43 node tmp; 44 j_=1; 45 int maxlas=0; 46 for(int i=1;i<=m;i++){ 47 tmp.idx=i; 48 for(int j=1;j<=n;j++){ 49 if(j>1) j_=(j_==n?1:j_+1); 50 while(!t[j_].empty()&&t[j_].front().idx+p<=i) t[j_].pop_front(); 51 tmp.val=maxlas-fix[j]+a[i][j]; 52 while(!t[j_].empty()&&tmp.val>=t[j_].back().val+sum[i][j_]-sum[t[j_].back().idx][j_]) t[j_].pop_back(); 53 t[j_].push_back(tmp); 54 f[j]=max(maxlas-fix[j]+a[i][j],t[j_].front().val+sum[i][j_]-sum[t[j_].front().idx][j_]); 55 } 56 maxlas=-1e9; 57 for(int j=1;j<=n;j++) maxlas=max(maxlas,f[j]); 58 } 59 printf("%d",maxlas); 60 return 0; 61 }