首页 > 代码库 > BZOJ1897 : tank 坦克游戏

BZOJ1897 : tank 坦克游戏

设$f[i][j][k]$表示坦克位于$(i,j)$,目前打了不超过$k$个位置的最大得分。

初始值$f[1][1][k]$为在$(1,1)$射程内最大$k$个位置的分数总和。

对于每次移动,会新增一行或者一列$O(R)$个位置,那么显然也是从大到小取。

暴力转移是$O(R)$的,不能接受,但是注意到这是个凸函数,故存在决策单调性,分治求解即可。

$ans=\max(f[i][j][T-i-j+2])$

时间复杂度$O(nm(T+R\log R))$。

 

#include<cstdio>#include<algorithm>using namespace std;const int N=505,M=255;int T,n,m,R,_n,_m,lim,o,i,j,k,a[N][N],f[2][N][M],g[M],v[M],q[N*N],cnt,ans;inline bool cmp(int x,int y){return x>y;}inline void up(int&x,int y){if(x<y)x=y;}void solve(int l,int r,int dl,int dr){  int m=(l+r)>>1,dm=dl,&f=v[m];  for(int i=dl;i<=dr&&i<=m;i++){    int t=g[m-i]+q[i];    if(t>f)f=t,dm=i;  }  if(l<m)solve(l,m-1,dl,dm);  if(r>m)solve(m+1,r,dm,dr);}int main(){  scanf("%d%d%d%d",&n,&m,&R,&T);  for(i=1;i<=n;i++)for(j=1;j<=m;j++)scanf("%d",&a[i][j]);  _n=max(1,n-R),_m=max(1,m-R);  for(i=1;i<=n&&i<=R+1;i++)for(j=1;j<=m&&j<=R+1;j++)if(a[i][j]>0)q[++cnt]=a[i][j];  if(cnt){    sort(q+1,q+cnt+1,cmp);    for(i=1;i<=cnt&&i<=T;i++)f[1][1][i]=f[1][1][i-1]+q[i];    for(;i<=T;i++)f[1][1][i]=f[1][1][i-1];  }  for(i=o=1;i<=_n;i++,o^=1)for(j=1;j<=_m;j++){    lim=T-i-j+2;    if(lim<=0)continue;    if(i>1){      for(k=0;k<=lim;k++)g[k]=v[k]=f[o^1][j][k];      cnt=0;      if(i+R<=n)for(k=max(1,j-R);k<=m&&k<=j+R;k++)if(a[i+R][k]>0)q[++cnt]=a[i+R][k];      if(cnt){        sort(q+1,q+cnt+1,cmp);        for(k=1;k<=cnt;k++)q[k]+=q[k-1];        solve(1,lim,0,cnt);      }      for(k=0;k<=lim;k++)f[o][j][k]=v[k];    }    if(j>1){      for(k=0;k<=lim;k++)g[k]=v[k]=f[o][j-1][k];      cnt=0;      if(j+R<=m)for(k=max(1,i-R);k<=n&&k<=i+R;k++)if(a[k][j+R]>0)q[++cnt]=a[k][j+R];      if(cnt){        sort(q+1,q+cnt+1,cmp);        for(k=1;k<=cnt;k++)q[k]+=q[k-1];        solve(1,lim,0,cnt);      }      for(k=0;k<=lim;k++)up(f[o][j][k],v[k]);    }    up(ans,f[o][j][lim]);  }  return printf("%d",ans),0;}

  

BZOJ1897 : tank 坦克游戏