51Nod 1487 思维+线段树

来源:互联网 发布:java数据库连接池写法 编辑:程序博客网 时间:2024/05/22 07:04

题目链接


题意:
有一个NM列的网格,每个格子有一个价值。
我们可以选择不多于两个格子建立塔,若塔的坐标为(x,y)
则我们可以收获(x+dx[1],y+dy[1]),(x+dx[2],y+dy[2])....(x+dx[k],y+dy[k])这些格子的价值。
同一个格子的价值只能收获一次。
问能获得的最大值。


思路:
才读完感觉像一个DP,但再仔细一想却感觉无从下手。

主要是处理重叠格子的价值只能取一次这个限制。

然后仔细推敲了一下样例,忽然发现,对于塔在点(x,y)所能收获的k个格子,点(x+1,y)所收获的k个格子只不过是上面的k个格子往下移动了一格(向下x轴正向,向右y轴正向)

所以重叠的情况跟dx[1],dy[1]....dx[k],dy[k]的值有关,故考虑O(nm)枚举每一个点(x,y),用线段树维护每一个点作为塔时所能收获的价值的最大值。

对于当前枚举的点,枚举其能收获的k个格子,令当前枚举的第i个点坐标为(nx,ny)

nx=x+dx[i]
ny=y+dy[i]

假设当某个点(tx,ty)作为塔时,该点会被重合,则
nx=tx+dx[j]
ny=ty+dy[j]

枚举j再做减法则可以将tx,ty算出,将其值从线段树中减去即可。

最后通过线段树得到区间最大值。
总复杂度:O(nmk2log(nm))

最后加上最优性剪枝,跑出来时间31ms

代码:

#include<cstdio>#include<cstdlib>#include<cstring>#include<queue>#include<algorithm>using namespace std;#define lson rt<<1#define rson rt<<1|1const int A = 2e4 + 10;const int B = 1e2 + 10;class Seg_Tree{public:    int l,r,Mx;}T[A<<2];int N,M,K,maze[B][B],dx[B],dy[B];char s[B];inline void push_up(int rt){    T[rt].Mx = max(T[lson].Mx,T[rson].Mx);}void build_Tree(int rt,int l,int r){    T[rt].l = l,T[rt].r = r;    if(l == r){        T[rt].Mx = 0;        return;    }    int mid = (l+r)>>1;    build_Tree(lson,l,mid);    build_Tree(rson,mid+1,r);    push_up(rt);}void update(int rt,int pos,int val){    int l = T[rt].l,r = T[rt].r;    if(l == r){        T[rt].Mx += val;        return;    }    int mid = (l+r)>>1;    if(pos<=mid) update(lson,pos,val);    else         update(rson,pos,val);    push_up(rt);}bool check(int x,int y){    if(x>=1 && x<=N && y>=1 && y<=M) return true;    return false;}int get_sum(int x,int y){    int sum = 0;    for(int i=1 ;i<=K ;i++){        if(check(x+dx[i],y+dy[i])) sum += maze[x+dx[i]][y+dy[i]];    }    return sum;}void solve(){    for(int x=1 ;x<=N ;x++){        for(int y=1 ;y<=M ;y++){            update(1,x*M+y,get_sum(x,y));        }    }    int Mx = 0;    for(int x=1 ;x<=N ;x++){        for(int y=1 ;y<=M ;y++){            int res = get_sum(x,y);            //printf("res = %d",res);            if(res + T[1].Mx <= Mx) continue;            for(int i=1 ;i<=K ;i++){                int n_x = x + dx[i],n_y = y + dy[i];                if(!check(n_x,n_y)) continue;                for(int j=1 ;j<=K ;j++){                    if(check(n_x-dx[j],n_y-dy[j])){                        update(1,(n_x-dx[j])*M+(n_y-dy[j]),-maze[n_x][n_y]);                    }                }            }            Mx = max(Mx,res + T[1].Mx);            //printf("Mx = %d\n",Mx);            for(int i=1 ;i<=K ;i++){                int n_x = x + dx[i],n_y = y + dy[i];                if(!check(n_x,n_y)) continue;                for(int j=1 ;j<=K ;j++){                    if(check(n_x-dx[j],n_y-dy[j])){                        update(1,(n_x-dx[j])*M+(n_y-dy[j]),maze[n_x][n_y]);                    }                }            }        }    }    printf("%d\n",Mx);}int main(){    //freopen("input","r",stdin);    int T;scanf("%d",&T);    while(T--){        scanf("%d%d%d",&N,&M,&K);        build_Tree(1,M+1,N*M+M);        for(int i=1 ;i<=N ;i++){            scanf("%s",s+1);            for(int j=1 ;j<=M ;j++){                maze[i][j] = s[j] - '0';            }        }        for(int i=1 ;i<=K; i++) scanf("%d%d",&dx[i],&dy[i]);        solve();    }    return 0;}