您的位置:首页 > 理论基础 > 计算机网络

BZOJ 2756 SCOI2012 奇怪的游戏

2017-08-10 16:00 330 查看

2756: [SCOI2012]奇怪的游戏

Time Limit: 40 Sec Memory Limit: 128 MB

Description

Blinker最近喜欢上一个奇怪的游戏。

这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数。每次 Blinker 会选择两个相邻

的格子,并使这两个数都加上 1。

现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成同

一个数则输出-1。

Input

输入的第一行是一个整数T,表示输入数据有T轮游戏组成。

每轮游戏的第一行有两个整数N和M, 分别代表棋盘的行数和列数。

接下来有N行,每行 M个数。

Output

对于每个游戏输出最少能使游戏结束的次数,如果永远不能变成同一个数则输出-1。

Sample Input

2

2 2

1 2

2 3

3 3

1 2 3

2 3 4

4 3 2

Sample Output

2

-1

HINT

【数据范围】

对于30%的数据,保证 T<=10,1<=N,M<=8

对于100%的数据,保证 T<=10,1<=N,M<=40,所有数为正整数且小于1000000000

分析:

首先我们考虑对棋盘黑白染色,那么我们发现:“每次相邻两个+1”,显然是一黑一白 +1

那么我们先统计出WhiteNum,BlackNum(黑白点的数目),WhiteSum,BlackSum(黑白点初始权值和)

那么对于一次增加,显然是WhiteSum+1,BlackSum+1

考虑对最后的情况进行讨论:

那么很显然,当WhiteNum==BlackNum时(即总点数为偶数)

如果WhiteSum!=BlackSum,显然无解

如果WhiteSum==BlackSum时,我们发现,对于X如果成立,那么X+1一定成立,显然满足二分的性质,那么二分这个值,进行判定

当WhiteNum!=BlackNum时(即总点数为奇数)

发现显然,若有解,则解唯一,那么直接验证正确性即可

至于解怎么求?

那么假设我们知道最后值为X,那么显然可以得到X∗WhiteNum−WhiteSum=X∗BlackNum−BlackSum

移项后显然可以化减出,eeeee,公式有点难写,自己化去吧

那么考虑建图:

S–>白点,约束为X-val[i][j]

黑点–>T,约束为X-val[i][j]

相邻的白点–>黑点,约束为INF

判断是否满流即可

#include<iostream>
#include<cstring>
#include<queue>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
inline int read(int &x){
x=0;int f=1;char c=getchar();
while(c>'9'||c<'0'){ if(c=='-') f=-1; c=getchar(); }
while(c>='0'&&c<='9'){ x=(x<<1)+(x<<3)+(c-'0'); c=getchar(); } x*=f;
}
#define MAXM 1001000
#define LL long long
#define MAXN 2010
int Cas,m,n,mat[50][50];
struct Edge{ int to,next;LL val; }e[MAXM];
int head[MAXN],tot=1;
inline void Add_Edge(int u,int v,LL w){
e[++tot].to=v;e[tot].val=w;
e[tot].next=head[u];head[u]=tot;
}
inline void Insert_Edge(int u,int v,LL w){ Add_Edge(u,v,w);Add_Edge(v,u,0); }
int dep[MAXN],cur[MAXN],S,T;
queue<int> q;
bool BFS(){
while(!q.empty()) q.pop();
memset(dep,-1,sizeof dep );
q.push(S);dep[S]=0;
while(!q.empty()){
int u=q.front();q.pop();
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(e[i].val&&dep[v]==-1){ dep[v]=dep[u]+1;q.push(v); }
}
}
return dep[T]!=-1;
}
#define INF 1LL<<60
inline bool ok(int x,int y){ return (x>=1&&x<=n&&y>=1&&y<=m); }
inline void init(){ tot=1;memset(head,0,sizeof head ); }
int col[50][50],id[50][50];
LL Sum_b,Sum_w,Num_w,Num_b,ID;
LL DFS(int u,LL flow){
if(u==T) return flow;
LL ret=0;
for(int i=cur[u];i;i=e[i].next){
int v=e[i].to;
if(e[i].val&&dep[v]==dep[u]+1){
LL w=DFS(v,min(flow-ret,e[i].val));
e[i].val-=w;e[i^1].val+=w;ret+=w;
if(e[i].val) cur[u]=i;
if(ret==flow) return ret;
}
}
if(!ret) dep[u]=-1;
return ret;
}
LL Dinic(){
LL ret=0;
while(BFS()){
for(int i=S;i<=T;i++) cur[i]=head[i];
ret+=DFS(S,INF);
}
return ret;
}
bool Check(LL x){
LL Tot=0; init();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
if(col[i][j]){
Insert_Edge(S,id[i][j],x-mat[i][j]);Tot+=(x-mat[i][j]);
if(ok(i-1,j)) Insert_Edge(id[i][j],id[i-1][j],INF);
if(ok(i,j-1)) Insert_Edge(id[i][j],id[i][j-1],INF);
if(ok(i,j+1)) Insert_Edge(id[i][j],id[i][j+1],INF);
if(ok(i+1,j)) Insert_Edge(id[i][j],id[i+1][j],INF);
}
else Insert_Edge(id[i][j],T,x-mat[i][j]);
}
LL ans=Dinic();
return Tot==ans;
}
void Build(){
S=0; T=n*m+1; Sum_b = Sum_w = Num_b = Num_w = 0; ID=0; int maxx=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
col[i][j]=(i+j)&1,id[i][j]= ++ID,maxx=max(maxx,mat[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
if(col[i][j]) Sum_w += mat[i][j],Num_w++;
else Sum_b += mat[i][j],Num_b++;
}
if(Num_w==Num_b&&Sum_b!=Sum_w){ puts("-1");return; }
if(Num_w==Num_b){
LL l=maxx,r=(1LL<<50);
while(l<=r){
LL Mid=(l+r)>>1;
if(Check(Mid)) r=Mid-1;
else l=Mid+1;
}
printf("%lld\n",l*Num_w-Sum_w);
} else {
LL x=(Sum_b-Sum_w)/(Num_b-Num_w);
if(x<maxx) {puts("-1");return;}
if(Check(x)) printf("%lld\n",x*Num_w-Sum_w);
else puts("-1");
}
}
int main(){
read(Cas);
while(Cas--){
read(n); read(m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) read(mat[i][j]);
Build();
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  网络流 二分答案