题意:
有一个
N
N
N行
M
M
M列的网格,每个格子有一个价值。
我们可以选择不多于两个格子建立塔,若塔的坐标为
(
x
,
y
)
(x,y)
(x,y)
则我们可以收获
(
x
+
d
x
[
1
]
,
y
+
d
y
[
1
]
)
,
(
x
+
d
x
[
2
]
,
y
+
d
y
[
2
]
)
.
.
.
.
(
x
+
d
x
[
k
]
,
y
+
d
y
[
k
]
)
(x+dx[1],y+dy[1]),(x+dx[2],y+dy[2])....(x+dx[k],y+dy[k])
(x+dx[1],y+dy[1]),(x+dx[2],y+dy[2])....(x+dx[k],y+dy[k])这些格子的价值。
同一个格子的价值只能收获一次。
问能获得的最大值。
思路:
才读完感觉像一个DP,但再仔细一想却感觉无从下手。
主要是处理重叠格子的价值只能取一次这个限制。
然后仔细推敲了一下样例,忽然发现,对于塔在点 ( x , y ) (x,y) (x,y)所能收获的 k k k个格子,点 ( x + 1 , y ) (x+1,y) (x+1,y)所收获的 k k k个格子只不过是上面的 k k k个格子往下移动了一格(向下 x x x轴正向,向右 y y y轴正向)
所以重叠的情况跟 d x [ 1 ] , d y [ 1 ] . . . . d x [ k ] , d y [ k ] dx[1],dy[1]....dx[k],dy[k] dx[1],dy[1]....dx[k],dy[k]的值有关,故考虑 O ( n m ) O(nm) O(nm)枚举每一个点 ( x , y ) (x,y) (x,y),用线段树维护每一个点作为塔时所能收获的价值的最大值。
对于当前枚举的点,枚举其能收获的
k
k
k个格子,令当前枚举的第
i
i
i个点坐标为
(
n
x
,
n
y
)
(nx,ny)
(nx,ny)
则
$nx = x + dx[i] $
n
y
=
y
+
d
y
[
i
]
ny = y + dy[i]
ny=y+dy[i]
假设当某个点
(
t
x
,
t
y
)
(tx,ty)
(tx,ty)作为塔时,该点会被重合,则
n
x
=
t
x
+
d
x
[
j
]
nx = tx + dx[j]
nx=tx+dx[j]
n
y
=
t
y
+
d
y
[
j
]
ny = ty + dy[j]
ny=ty+dy[j]
枚举 j j j再做减法则可以将 t x , t y tx,ty tx,ty算出,将其值从线段树中减去即可。
最后通过线段树得到区间最大值。
总复杂度:
O
(
n
m
k
2
l
o
g
(
n
m
)
)
O(nmk^2log(nm))
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|1
const 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;
}