2756: [SCOI2012]奇怪的游戏
Time Limit: 40 Sec Memory Limit: 128 MB
Submit: 490 Solved: 114
[Submit][Status][Discuss]
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
Source
day2
解析:
将所有格子进行黑白染色。。。因为每次对相邻的两个格子加1,所以相邻的两个格子肯定属于二分图的两边。。。
然后一边的格子连源点,容量为我们二分出的所有格子最后变成的值x-map[i][j],另一边同样连汇点。。。中间相邻的格子连容量无穷的边。。。
以上是网络流建图部分。。。
首先,如果有奇数个格子。。。计算出黑白格子总和的差值diff,如果diff<maxw输-1
否者,跑最大流,如果能跑满,即flow(diff)==(diff*n*m-sum)>>1;输出等式右边部分,否者-1.。。
如果有偶数个格子。。。
我们可以证明当所有格子最后变成一个数成立,那么大于这个数的所有情况都成立。。。
于是很容易想到二分,分到一个值,如果满足就在左区间找否者就在右区间找。。。
注意本题会超int
ps:据说本题卡递归的sap,其实不是。。。我就是用递归sap,但需要小优化一下。。。要把sap的 if(d[0]>=l+2)return res;这句话放到循环里面
这儿太坑了,整整Debug了两天。。。
代码:
/**************************************************************
Problem: 2756
User: jianing
Language: C++
Result: Accepted
Time:11884 ms
Memory:3192 kb
****************************************************************/
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long LL;
const LL inf=0x3f3f3f3f3f3f3f3fll;
#define most 1000000000
int n,m,l=0,e=0;
int map[50][50],val[2510];
struct node
{
int u,v,next;
LL c;
}edge[100000];
int head[100000];
int d[2500],sumd[2500];
int maxx;
LL sum1,sum2;
void read()
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
}
void add(int u,int v,LL c)
{
edge[e].u=u;
edge[e].v=v;
edge[e].c=c;
edge[e].next=head[u];
head[u]=e++;
edge[e].u=v;
edge[e].v=u;
edge[e].c=0;
edge[e].next=head[v];
head[v]=e++;
}
LL sap(int u,LL flow)
{
if(u==l+1)return flow;
LL res=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(edge[i].c && d[u]==d[v]+1)
{
LL t=sap(v,(LL)min(edge[i].c,(flow-res)));
res+=t;
edge[i].c-=t;edge[i^1].c+=t;
if(res==flow)return res;
if(d[0]>=l+2)return res;//本来放在下面,T哭了
} //
} //
sumd[d[u]]--; //就这儿
if(sumd[d[u]]==0)d[0]=l+2;
sumd[++d[u]]++;
return res;
}
LL flow(LL x)
{
memset(head,-1,sizeof(head));
memset(edge,0,sizeof(edge));
e=0;l=n*m;int t=n*m+1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if((i+j)&1)
{
add(0,m*(i-1)+j,x-map[i][j]);
if(i>1)add(m*(i-1)+j,m*(i-2)+j,inf);
if(i<n)add(m*(i-1)+j,m*i+j,inf);
if(j>1)add(m*(i-1)+j,m*(i-1)+j-1,inf);
if(j<m)add(m*(i-1)+j,m*(i-1)+j+1,inf);
}
else add(m*(i-1)+j,t,x-map[i][j]);
}
memset(d,0,sizeof(d));
memset(sumd,0,sizeof(sumd));
sumd[0]=l+2;
LL ans=0;
while(d[0]<=l+1)
ans+=sap(0,inf);
return ans;
}
void check()
{
LL best=inf;
LL ll=(LL)maxx,rr=inf;
while(ll<rr)
{
LL mid=(ll+rr)>>1;
LL ans=flow(mid);
LL tmp=mid*n*m-sum1-sum2;
if(tmp==ans*2)
{
if(ans>0)best=min(best,ans);
rr=mid;
}
else ll=mid+1;
}
printf("%lld\n",best);
}
LL max(LL a,LL b)
{
return a>b?a:b;
}
void work()
{
e=0;maxx=0;
sum1=0;sum2=0;int cnt1=0,cnt2=0;
memset(map,0,sizeof(map));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%d",&map[i][j]);
maxx=max(maxx,map[i][j]);
if((i+j)&1)
{
sum1+=map[i][j];
cnt1++;
}
else
{
sum2+=map[i][j];
cnt2++;
}
}
if(cnt1!=cnt2)
{
if((sum1-sum2)%(cnt1-cnt2)!=0)printf("-1\n");
LL diff=(sum1-sum2)/(cnt1-cnt2);
if(diff<maxx)
{
printf("-1\n");
return ;
}
else
{
LL ans=flow(diff);
LL tmp=diff*n*m-sum1-sum2;
if(tmp==ans*2)printf("%lld\n",ans);
else printf("-1\n");
}
}
else check();
}
int main()
{
// read();
int test;
scanf("%d",&test);
while(test--)
work();
return 0;
}