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;
}

 

posted @ 2019-08-15 18:18  kafuuchino  阅读(193)  评论(0编辑  收藏  举报