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$个的最大价值

注意到每步打目标的个数一定是单调不降的

每次转移时用决策单调性分治优化

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

原文地址:https://www.cnblogs.com/kafuuchino/p/11359685.html

时间: 2024-11-06 03:55:02

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> #in

1897. tank 坦克游戏

传送门 显然考虑 $dp$,发现时间只和当前位置和攻击次数有关,设 $F[i][j][k]$ 表示当前位置为 $i,j$ ,攻击了 $k$ 次得到的最大分数 初始 $f[1][1][k]$ 为位置 $1,1$ 能打到的前 $k$ 大位置的分数和 每次移动都会多一行或多一列目标可以选择,攻击时显然优先攻击分数大的位置,因为要排序,单次转移就是 $O(R \log R)$ 的 加上原本 $i,j,k$ 复杂度 $O(nmTR \log R)$ ,考虑优化 先考虑从 $f[i][j-1][k-t]$

P3515 [POI2011]Lightning Conductor(决策单调性分治)

P3515 [POI2011]Lightning Conductor 式子可转化为:$p>=a_j-a_i+sqrt(i-j) (j<i)$ $j>i$的情况,把上式翻转即可得到 下面给一张图证明这是满足决策单调性的 把$a_j+sqrt(i-j)$表示在坐标系上 显然$sqrt(i-j)$的增长速度趋缓 曲线$a$被曲线$b$超过后是无法翻身的 对两个方向进行决策单调性分治,取$max$即可 #include<iostream> #include<cstdio>

bzoj2216: [Poi2011]Lightning Conductor(分治决策单调性优化)

每个pi要求 这个只需要正反DP(?)一次就行了,可以发现这个是有决策单调性的,用分治优化 #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; const int maxn=500010,inf=1e9; int n; int a[

Codeforces 868F. Yet Another Minimization Problem【决策单调性优化DP】【分治】【莫队】

LINK 题目大意 给你一个序列分成k段 每一段的代价是满足\((a_i=a_j)\)的无序数对\((i,j)\)的个数 求最小的代价 思路 首先有一个暴力dp的思路是\(dp_{i,k}=min(dp_{j,k}+calc(j+1,i))\) 然后看看怎么优化 证明一下这个DP的决策单调性: trz说可以冥想一下是对的就可以 所以我就不证了 (其实就是决策点向左移动一定不会更优) 然后就分治记录当前的处理区间和决策区间就可以啦 //Author: dream_maker #include<bi

CodeForces 868F Yet Another Minimization Problem(决策单调性优化 + 分治)

题意 给定一个序列 \(\{a_1, a_2, \cdots, a_n\}\),要把它分成恰好 \(k\) 个连续子序列. 每个连续子序列的费用是其中相同元素的对数,求所有划分中的费用之和的最小值. \(2 \le n \le 10^5, 2 \le k \le \min(n, 20), 1 \le a_i \le n\) 题解 \(k\) 比较小,可以先考虑一个暴力 \(dp\) . 令 \(dp_{k, i}\) 为前 \(i\) 个数划分成 \(k\) 段所需要的最小花费. 那么转移如下

决策单调性优化dp

决策单调性: 对于一些dp方程,经过一系列的猜想和证明,可以得出,所有取的最优解的转移点(即决策点)位置是单调递增的. 即:假设f[i]=min(f[j]+b[j]) (j<i) 并且,对于任意f[i]的决策点g[i],总有f[i+1]的决策点g[i+1]>=g[i](或者<=g[i]) 那么,这个方程就具备决策单调性. 这个有什么用吗? 不懂具体优化方法的话确实也没有什么用.可能还是n^2的.只不过范围可能少了一些. 经典入门例题: Description: [POI2011]Ligh

Gym - 101002H: Jewel Thief (背包,分组,DP决策单调性)

pro:给定N,M.输入N个物品,(si,vi)表示第i个物品体积为si,价值为vi,s<=300,vi<=1e9: N<1e6:现在要求,对于背包体积为1到M时,求出最大背包价值. sol:显然直接跑背包会爆炸. 发现物品体积都比较小,我们先对相同体积的排序,对于体积相同的一起处理. 然后发现转移都是在差为体积整数倍之间,按照容量对体积取模分组 ,发现组内部转移满足神奇的决策单调性. 然后就是s次分治. O(KClogC) #include<bits/stdc++.h> #

决策单调性优化dp 专题练习

决策单调性优化dp 专题练习 优化方法总结 一.斜率优化 对于形如 \(dp[i]=dp[j]+(i-j)*(i-j)\)类型的转移方程,维护一个上凸包或者下凸包,找到切点快速求解 技法: 1.单调队列 : 在保证插入和查询的x坐标均具有单调性时可以使用 2.单调栈+二分:保证插入有单调性,不保证查询有单调性 3.分治+ 1 或 2:在每次分治时将\([l,mid]\)这段区间排序后插入,然后更新右区间\([mid+1,r]\)的答案 二.分治.单调队列维护有单调性的转移 (甚至还有分治套分治)