BZOJ 2756: [SCOI2012]奇怪的游戏 网络流/二分

 

2756: [SCOI2012]奇怪的游戏

Time Limit: 40 Sec  Memory Limit: 128 MB
Submit: 1594  Solved: 396
[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

 

题解:

我们把整个棋盘的格子分为两种,一种为白,一种为黑

然后设最后的格子全部变成了x,那么x*num1-sum1=x*num2-sum2;

其中num1为白色格子数量,num2位黑色格子数量,sum1为白色格子权值和

那么,我们可以得到x=(sum1-sum2)/(num1-num2)

当num1!=num2的时候,我们可以直接通过最大流来check

 

否则的话,我们就二分枚举答案

首先如果x能够成立的话,那么大与x的所有数都能成立,只要再铺一层就好,而且num1+num2%2==0

 

所以题目的思路还是比较简单的~

#include<iostream>

#include<cstdio>

#include<cstring>

#include<cstdlib>

#include<set>

#include<ctime>

#include<vector>

#include<queue>

#include<algorithm>

#include<map>

#include<cmath>

#define inf (1LL<<50)

#define pa pair<int,int>

#define ll long long 

#define p(x,y) (x-1)*m+y

using namespace std;

int read()

{

    int x=0,f=1;char ch=getchar();

    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}

    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}

    return x*f;

}

ll s0,s1;

int c0,c1;

int test,n,m,cnt,S,T;

int xx[4]={0,0,1,-1},yy[4]={1,-1,0,0};

int a[45][45];

int last[2005],h[2005],q[2005],cur[2005];

bool color[45][45];

struct edge{

    int to,next;ll v;

}e[20005];

void insert(int u,int v,ll w)

{

    e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;e[cnt].v=w;

    e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;e[cnt].v=0;

}

bool bfs()

{

    int head=0,tail=1;

    memset(h,-1,sizeof(h));

    q[0]=S;h[S]=0;

    while(head!=tail)

    {

        int now=q[head];head++;

        for(int i=last[now];i;i=e[i].next)

            if(e[i].v&&h[e[i].to]==-1)

            {

                h[e[i].to]=h[now]+1;

                q[tail++]=e[i].to;

            }

    }

    return h[T]!=-1;

}

ll dfs(int x,ll f)

{

    if(x==T)return f;

    ll w,used=0;

    for(int i=cur[x];i;i=e[i].next)

        if(h[e[i].to]==h[x]+1)

        {

            w=dfs(e[i].to,min(f-used,e[i].v));

            e[i].v-=w;e[i^1].v+=w;

            if(e[i].v)cur[x]=i;

            used+=w;if(used==f)return f;

        }

    if(!used)h[x]=-1;

    return used;

}

ll dinic()

{

    ll tmp=0;

    while(bfs())

    {

        for(int i=S;i<=T;i++)cur[i]=last[i];

        tmp+=dfs(S,inf);

    }

    return tmp;

}

bool check(ll x)

{

    memset(last,0,sizeof(last));

    cnt=1;S=0;T=n*m+1;

    ll tot=0;

    for(int i=1;i<=n;i++)

        for(int j=1;j<=m;j++)

            if(color[i][j])

            {

                insert(S,p(i,j),x-a[i][j]);tot+=x-a[i][j];

                for(int k=0;k<4;k++)

                {

                    int nowx=i+xx[k],nowy=j+yy[k];

                    if(nowx<1||nowy<1||nowx>n||nowy>m)continue;

                    insert(p(i,j),p(nowx,nowy),inf);

                }

            }

            else insert(p(i,j),T,x-a[i][j]);

    if(dinic()==tot)return 1;

    return 0;

}

int main()

{

    test=read();

    while(test--)

    {

        c0=c1=s0=s1=0;

        n=read();m=read();

        int mx=0;

        for(int i=1;i<=n;i++)

            for(int j=1;j<=m;j++)

            {

                a[i][j]=read(),color[i][j]=(i+j)&1;

                mx=max(mx,a[i][j]);

            }

        for(int i=1;i<=n;i++)

            for(int j=1;j<=m;j++)

                if(color[i][j])s1+=a[i][j],c1++;

                else s0+=a[i][j],c0++;

        if(c0!=c1)

        {

            ll x=(s0-s1)/(c0-c1);

            if(x>=mx)

                if(check(x))

                {

                    printf("%lld\n",x*c1-s1);

                    continue;

                }

            puts("-1");

        }

        else 

        {

            ll l=mx,r=inf;

            while(l<=r)

            {

                ll mid=(l+r)>>1;

                if(check(mid))r=mid-1;

                else l=mid+1;

            }

            printf("%lld\n",(ll)l*c1-s1);

        }

    }    

    return 0;

}

 

你可能感兴趣的:(2012)