bzoj1897. tank 坦克游戏(决策单调性分治)
题目描述
有这样一款新的坦克游戏。在游戏中,你将操纵一辆坦克,在一个N×M的区域中完成一项任务。在此的区域中,将会有许多可攻击的目标,而你每摧毁这样的一个目标,就将获得与目标价值相等的分数。只有获得了最高的分数,任务才算完成。同时,为了增加游戏的真实性和难度,该游戏还做了以下的限制:
1)坦克有射程r的限制。为方便计算,射程r规定为:若坦克位于(x, y)格,则它可攻击的目标(x1, y1)必须满足|x-x1|, |y-y1|∈[0, r]。
2)对坦克完成任务的时间有严格限制,规定为t秒。其中,坦克每进行一次移动都需1秒的时间,每攻击一个目标也需1秒的时间。时间一到t秒,便对此次任务进行记分。
3)坦克最初位于左上角,且移动方向只准是向右或向下,每次只允许移动一格。
在以上的限制条件下,要完成该任务便成为了一件很难事情。因此,你必须为此编写一个程序,让它助你完成这个艰巨的任务。
输入格式
第一行四个整数N、M、r、t,分别表示区域的长、宽,以及射程和完成任务时间。
接下来N行是一个N×M的矩阵,对应每个位置上目标的价值。
输出格式
输出文件仅一个数max,即该任务中可得到的最高分数。
样例
样例输入
5 5 2 7
0 5 0 0 4
0 0 0 0 2
0 0 0 0 0
0 0 0 0 0
5 0 3 0 11
样例输出
21
数据范围与提示
1≤N、M≤500,1≤r≤100,1≤t≤250。
对于20%的数据有:1≤N、M≤10。
对于60%的数据有:1≤N、M≤50,1≤r≤10。
对于80%的数据有:1≤N、M≤100,1≤r≤20。
设$f[i][j][k]$表示到$(i,j)$为止打$k$个的最大价值
注意到每步打目标的个数一定是单调不降的
证明....挂一神犇的blog
每次转移时用决策单调性分治优化
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define re register using namespace std; int read(){ char c=getchar(); int x=0; while(c<'0'||c>'9') c=getchar(); while('0'<=c&&c<='9') x=x*10+c-48,c=getchar(); return x; } #define N 505 int cmp(int A,int B){return A>B;} int n,m,R,T,ans,k=1,a[N][N],f[2][N][253],h[N*N],tp,g[N],v[N]; void solve(int l,int r,int dl,int dr){ if(l>r||dl>dr) return ; int mm=(l+r)/2,dm=dl; for(int i=dl;i<=min(mm,dr);++i) if(v[mm]<g[mm-i]+h[i]) v[mm]=g[mm-i]+h[i],dm=i; solve(l,mm-1,dl,dm); solve(mm+1,r,dm,dr); } int main(){ n=read(); m=read(); R=read(); T=read(); for(re int i=1;i<=n;++i) for(re int j=1;j<=m;++j) a[i][j]=read(); for(re int i=1;i<=R+1;++i) for(re int j=1;j<=R+1;++j) if(a[i][j]) h[++tp]=a[i][j]; sort(h+1,h+tp+1,cmp); tp=min(tp,T);//注意最多取T个 for(re int i=1;i<=tp;++i) f[1][1][i]=f[1][1][i-1]+h[i]; int _n=max(1,n-R),_m=max(1,m-R); for(re int i=1;i<=_n;++i,k^=1) for(re int j=1;j<=_m;++j){ int lim=T-i-j+2; if(lim<=0) continue; if(i>1){ for(re int u=0;u<=lim;++u) v[u]=g[u]=f[k^1][j][u]; tp=0; for(re int u=max(1,j-R);u<=min(m,j+R);++u) if(a[i+R][u]) h[++tp]=a[i+R][u]; sort(h+1,h+tp+1,cmp); for(re int u=1;u<=tp;++u) h[u]+=h[u-1]; solve(1,lim,0,tp); for(re int u=0;u<=lim;++u) f[k][j][u]=max(f[k][j][u],v[u]); } if(j>1){ for(re int u=0;u<=lim;++u) v[u]=g[u]=f[k][j-1][u]; tp=0; for(re int u=max(1,i-R);u<=min(n,i+R);++u) if(a[u][j+R]) h[++tp]=a[u][j+R]; sort(h+1,h+tp+1,cmp); for(re int u=1;u<=tp;++u) h[u]+=h[u-1]; solve(1,lim,0,tp); for(re int u=0;u<=lim;++u) f[k][j][u]=max(f[k][j][u],v[u]); } while(lim) ans=max(ans,f[k][j][lim--]); } printf("%d",ans); return 0; }