时间限制:1秒 内存限制:64M
【问题描述】
Blinker最近喜欢上一个奇怪的游戏。这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数。每次 Blinker 会选择两个相邻的格子,并使这两个数都加上1。 现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成同一个数则输出-1。
【输入格式】
输入的第一行是一个整数T,表示输入数据有T轮游戏组成。
每轮游戏的第一行有两个整数N和M, 分别代表棋盘的行数和列数。接下来有N行,每行 M个数。
【输出格式】
对于每个游戏输出最少能使游戏结束的次数,如果永远不能变成同一个数则输出-1。
【输入样例】
2
2 2
1 2
2 3
3 3
1 2 3
2 3 4
4 3 2
【输出样例】
2
-1
【数据范围】
对于30%的数据,保证 T<=10,1<=N,M<=8
对于100%的数据,保证 T<=10,1<=N,M<=40,所有数为正整数且小于1000000000
【来源】
BZOJ2756
这道题很明显是一道网络流但是要做起来就麻烦了,首先我们要把点分成黑白点(二分图的普遍做法),建图就是白进黑出(具体的容量下文会提到),然后白向黑连边。
图是建立好了,但要做这道题我们还差一个很关键的东西,就是题中要的那个要变成的数,很多人会直接用最大的那个,但其实并不是。
我们可以来算一下那个值的可能最小值:
我们设这个值为x,白点的数为x1,黑点的数为x2,白点的权值和为y1,黑点的权值和为y2.
所以:x*x1-y1=x*x2-y2.(因为白点加一次黑点就要加一次)
但最后的答案已经不是这个x,因为会出现1白1黑没到x,但又刚好不能挨着,而其他点已经到x不能加的情况。
但我们可以证明如果黑白点数量不一样那么如果x不行,其他的大于x的值也不行。
那接下来我们就只需要考虑黑白点数量一样的情况了。
对于这种情况我们可以证明只要k成立那么所有大于k的值都可以成立。
到这里你想到了什么?
我们伟大的二分拼人品大法,接下来就是2分的范围了。
而没时间的我直接用最大点的值加了个100,再在这个值与最大值直接2分猜答案了一波。事实证明可以秒过而且不会错。
代码如下:
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn=45;
const ll inf=20000000ll;
struct edge
{
int u,v;
ll f,c;
};
vector<edge>e;
vector<int>g[maxn*maxn];
int q[maxn*maxn*5],cur[maxn*maxn],d[maxn*maxn],n,m,a[maxn][maxn],s,t,cnt=-1;
ll tot;
bool ok=0;
int dx[]={0,-1,0,1};
int dy[]={-1,0,1,0};
int vis[maxn][maxn];
ll num[3],sum[3];
void dfs2(int i,int j,int tt)
{
vis[i][j]=tt;
for(int k=0;k<4;k++)
{
int x=i+dx[k],y=j+dy[k];
if(x<1||x>n||y<1||y>m||vis[x][y]) continue;
dfs2(x,y,3-tt);
}
}
void add(int u,int v,ll f)
{
e.push_back((edge){u,v,f,0});
g[u].push_back(++cnt);
e.push_back((edge){v,u,0,0});
g[v].push_back(++cnt);
}
int ask(int i,int j)
{
return (i-1)*m+j;
}
bool bfs()
{
int root=0,frond=0;
memset(d,0,sizeof(d));
q[root++]=s;
d[s]=1;
while(root!=frond)
{
int i=q[frond++];
int tt=g[i].size();
for(int k=0;k<tt;k++)
{
int id=g[i][k],j=e[id].v;
if(e[id].c==e[id].f||d[j]) continue;
d[j]=d[i]+1;
q[root++]=j;
}
}
return d[t]!=0;
}
ll dfs(int i,ll a)
{
if(i==t||a==0) return a;
ll f,flow=0;
int tt=g[i].size();
for(int &k=cur[i];k<tt;k++)
{
int id=g[i][k],j=e[id].v;
if(d[j]==d[i]+1&&(f=dfs(j,min(a,e[id].f-e[id].c)))>0)
{
a-=f;
flow+=f;
e[id].c+=f;
e[id^1].c-=f;
if(!a) break;
}
}
return flow;
}
ll dinic()
{
ll flow=0;
while(bfs())
{
memset(cur,0,sizeof(cur));
flow+=dfs(s,inf);
}
return flow;
}
void work(int maxv)
{
cnt=-1;
e.clear();
for(int i=s;i<=t;i++)
g[i].clear();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(a[i][j]<maxv)
{
if(vis[i][j]==2)add(ask(i,j),t,maxv-a[i][j]);
if(vis[i][j]==1)
{
add(s,ask(i,j),maxv-a[i][j]);
for(int k=0;k<4;k++)
{
int x=i+dx[k],y=j+dy[k];
if(x<1||x>n||y<1||y>m) continue;
add(ask(i,j),ask(x,y),200000000ll);
}
}
}
}
}
void init()
{
int maxv=0;
scanf("%d%d",&n,&m);
s=0,t=n*m+1;
memset(vis,0,sizeof(vis));
memset(num,0,sizeof(num));
memset(sum,0,sizeof(sum));
dfs2(1,1,1);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
num[vis[i][j]]++;
sum[vis[i][j]]+=a[i][j];
maxv=max(maxv,a[i][j]);
}
if(num[1]!=num[2])
{
maxv=(sum[1]-sum[2])/(num[1]-num[2]);
work(maxv);
ll ans=dinic();
if(ans+sum[2]==num[2]*maxv)
cout<<ans<<endl;
else printf("-1\n");
}
else
{
if(sum[2]!=sum[1]){
printf("-1\n");
return;
}
int A=maxv;
int B=maxv+100;
ll ans2=-1;
while(A<=B)
{
int C=(A+B)>>1;
work(C);
ll ans=dinic();
if(ans+sum[2]==num[2]*C) ans2=ans,B=C-1;
else A=C+1;
}
cout<<ans2<<endl;
}
}
int main()
{
//freopen("game.in","r",stdin);
//freopen("game.out","w",stdout);
int T;
scanf("%d",&T);
while(T--)
{
init();
}
return 0;
}