[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
1 2
2 3
3 3
1 2 3
2 3 4
4 3 2
Sample Output
-1
HINT
【数据范围】
对于30%的数据,保证 T<=10,1<=N,M<=8
对于100%的数据,保证 T<=10,1<=N,M<=40,所有数为正整数且小于1000000000
说实话,真看不出来是网络流,后来HNY说二分答案我才反应过来可以二分最后每个格子最后的值,然后验证
但是格子总数是奇数的时候要单独考虑
(下面引用njlcazl http://blog.csdn.net/njlcazl/article/details/8732646)
由于每次操作都是相邻的两个,所以可以考虑将棋盘黑白染色,这样我们可以对黑色的格子和白色的格子单独考虑。
设黑色格子个数为cnt1,总和为sum1,白色格子个数为cnt2,总和为sum2,最终所有格子都变成了x,则很容易写出下列的关系式:
cnt1 *x - sum1 = cnt2 * x - sum2
=> x * (cnt1 - cnt2) = sum1 - sum2
=> x = (sum1 - sum2) / (cnt1 - cnt2)
现在讨论cnt1 与 cnt2的情况:
1、若cnt1 = cnt2,则说明在sum1 = sum2的情况下,若x可行,则x+1同样可以,这样我们可以通过二分的方法找出最小的满足条件的x,求出最小步数;若sum1 != sum2则无解。
2、若cnt1!= cnt2,则我们解出了一个x
这个x必须要满足三个条件:
(1)x必须为整数
(2)x不小于所有格子的最大值
(3)x必须要通过检验
如果x满足这三个条件,则我们可以解出最小步数,否则无解。
那么我们怎样检验x是否可行呢,这个需要通过网络流来验证。
设map[i][j]为格子对应的数字,对于每个黑色的格子,从源点连一条边,容量为x - map[i][j],并且对它周围的四个白色格子连一条边容量为inf;对于每个白色格子,向汇点连一条边,容量为x - map[i][j]
设最大流为maxflow,若maxflow = (n * m - sum1 - sum2) >> 1的话,说明存在方案可以使所有数变成x,同时最小步数minstep = (n * m - sum1 - sum2) >> 1
注意到格子的数可能很大,所以要使用long long类型
这样就解决了本题。
(引用结束)
然后,上面虽然是cnt1-cnt2,但是我们可以知道在奇数个的时候cnt1-cnt2一定等于1
然后网上都说这一题卡递归的sap或递归Dinic,必须写非递归的,但是HNY大神说他递归的也过了,然后我也写递归。。。。。调了两天,还是 T 。。。。。
结果今天他猛然记起他把递归sap优化了一下 http://blog.csdn.net/jiangzh7/article/details/8892588 ,时间效率优于非递归!!!
然后果断把他A了
测评情况(BZOJ)
在本机一直WA和T,本来就快改成非递归了,HNY大神说可以优化,然后就很兴奋的A了~
C++ AC Code
/*http://blog.csdn.net/jiangzh7
By Jiangzh*/
#include<cstdio>
#include<cstring>
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
typedef long long LL;
const int N=40+10;
const int NN=50*50;
const int dx[]={0,0,-1,1};
const int dy[]={1,-1,0,0};
const int inf=0x3f3f3f3f;
const LL infll=0x3f3f3f3f3f3f3f3fLL;
int n,m;
int map[N][N],color[N][N];
struct EG{int y;LL flow;int next;}edge[NN*10];
int head[NN],L;
int v[NN],h[NN];
int S,T;
LL num[2],sum[2],maxp=0;
void read()
{
scanf("%d%d",&n,&m);
int cc=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%d",&map[i][j]);
color[i][j]=!color[i][j-1];
if(j==1) color[i][j]=(cc=!cc);
num[color[i][j]]++;
sum[color[i][j]]+=map[i][j];
maxp=max(maxp,map[i][j]);
}
}
inline int get(int x,int y) { return (x-1)*m+y; }
LL sap(int x,LL flow)
{
if(x==T) return flow;
LL res=0;
for(int i=head[x];i!=-1;i=edge[i].next)
if(edge[i].flow && h[x]==h[edge[i].y]+1)
{
LL t=sap(edge[i].y,min(edge[i].flow,flow-res));
edge[i].flow-=t;
edge[i^1].flow+=t;
if((res+=t)>=flow) return res;
if(h[S]>T) return res;
}
if((--v[h[x]])==0) h[S]=T+1;
++v[++h[x]];
return res;
}
void inlink(int x,int y,LL z)
{
edge[L]=(EG){y,z,head[x]};
head[x]=L++;
edge[L]=(EG){x,0,head[y]};
head[y]=L++;
}
bool check(LL final)
{
memset(edge,0,sizeof(edge));
memset(head,-1,sizeof(head));L=0;
memset(v,0,sizeof(v));
memset(h,0,sizeof(h));
S=0;T=get(n,m)+1;
LL total=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
LL tmp=final-map[i][j];
if(color[i][j]==0) inlink(S,get(i,j),tmp);
else inlink(get(i,j),T,tmp);
total+=tmp;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;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;
if (color[i][j]==0) inlink(get(i,j),get(x,y),infll);
}
v[0]=T+1;
LL maxflow=0;
while(h[S]<T+1) maxflow+=sap(S,infll);
return total==(maxflow*2);
}
inline LL getans(LL x)
{
return ((LL)n*m*x-sum[0]-sum[1])/2;
}
void work()
{
if((n*m)%2==0)
{
LL L=1,R=infll/1000,M;
int ans=-1;
while(L<=R)
{
M=L+((R-L)>>1);
if(check(M)) R=M-1,ans=M;
else L=M+1;
}
if(ans==-1) printf("%d\n",ans);
else printf("%lld\n",getans(ans));
}
else{
bool flag=0;
LL x=(sum[0]-sum[1]);
if(x>=maxp && check(x)) flag=1;
if(flag) printf("%lld\n",getans(x));
else printf("-1\n");
}
}
int main()
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
int T; scanf("%d",&T);
while(T--)
{
memset(map,0,sizeof(map));
memset(color,0,sizeof(color));
sum[0]=sum[1]=num[0]=num[1]=maxp=0;
read();
work();
}
return 0;
}