一、题目
二、解法
根据部分分的提示,我们可以想到如果 max ( x i − x j , y i − y j ) ≥ 2 \max(x_i-x_j,y_i-y_j)\geq2 max(xi−xj,yi−yj)≥2,那么这两个点是毫无瓜葛的,我们可以继续用这种方法来分类讨论所有情况。
情况
1
1
1:如果
max
(
x
i
−
x
j
,
y
i
−
y
j
)
=
1
\max(x_i-x_j,y_i-y_j)=1
max(xi−xj,yi−yj)=1,这两个点是在九宫格内的,看图:
不难发现就算有两种选择他们覆盖的点都是一样的,而且十字架重合的部分是 2 2 2 个格子。
情况 2 2 2:如果 max ( x i − x j , y i − y j ) = 2 \max(x_i-x_j,y_i-y_j)=2 max(xi−xj,yi−yj)=2,可以选择十字架区域的任意一个点空出来,并且这个情况十字架重合的格子是 1 1 1 个。
设重复的个数是 A A A,一定区域内选择会相互影响的关键点个数是 B B B,不难发现有 A ≤ B A\leq B A≤B,因为重复的地方都要求某个点留一个空给他,这样的机会是 B B B次的。然后因为这些关键点会互相影响,所以 A ≥ B − 1 A\geq B-1 A≥B−1,最少一个重复的格子可以连接两个关键点。这样一来, A A A的取值就很少了。
- 如果 A = B A=B A=B,那么这些涉及到的格子都要选。
- 如果 A = B − 1 A=B-1 A=B−1,那么是可以随性所欲地空出来一个格子的,空出来权值最小的格子即可。
所以实现的时候以 d f s \tt dfs dfs 的形式写,时间复杂度 O ( n m ) O(nm) O(nm)
#include <cstdio>
#include <vector>
using namespace std;
const int M = 1000005;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,k,ans,vis[M],fl[M];vector<int> a[M];
int val,dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
int dfs(int x,int y,int &val)
{
vis[x*m+y]=1;
val++;
if(x==0 || x==n-1) val--;//这里"机会"会被消耗
if(y==0 || y==m-1) val--;
if(val<0) return -1;
ans+=a[x][y];
int Mi=1005;
//九宫格内的情况
for(int i=x-1;i<=x+1;i++)
for(int j=y-1;j<=y+1;j++)
{
if(i<0 || j<0 || i>=n || j>=m) continue;
if(fl[i*m+j] && !vis[i*m+j])
{
ans-=a[x][j];
ans-=a[i][y];
int res=dfs(i,j,val-=2);
if(res==-1) return -1;
Mi=min(Mi,res);
}
}
//外面的四个特殊点
for(int d=0;d<4;d++)
{
int i=x+dx[d],j=y+dy[d];
if(i<0 || j<0 || i>=n || j>=m) continue;
ans+=a[i][j];Mi=min(Mi,a[i][j]);
i+=dx[d];j+=dy[d];
if(i<0 || j<0 || i>=n || j>=m || !fl[i*m+j]) continue;
if(x*m+y<i*m+j)//小->大,只算一次
{
val--;
ans-=a[(i+x)/2][(j+y)/2];
}
if(vis[i*m+j])
{
if(val<0) return -1;
continue;
}
int res=dfs(i,j,val);
if(res==-1) return -1;
Mi=min(Mi,res);
}
return Mi;
}
int main()
{
n=read();m=read();
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
a[i].push_back(read());
k=read();
for(int i=1;i<=k;i++)
{
int x=read(),y=read();
fl[x*m+y]=1;
}
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
if(!vis[i*m+j] && fl[i*m+j])
{
val=0;int res=dfs(i,j,val);
if(res==-1)
{
puts("No");
return 0;
}
if(val) ans-=res;
}
printf("%d\n",ans);
}