奇怪的游戏

奇怪的游戏

题目描述

 

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 

 

solution

对棋盘进行黑白染色
设黑格个数为num1 数值和为sum1
设白格个数为num1 数值和为sum1

设最后都变为x

num1 * x – sum1 = num2 * x – sum2
x = (sum1 – sum2) / (num1 – num2)
当num1 ≠ num2时 可以解出 x 再用网络流check即可

对于num1 = num2时 可以发现 对于一个合法的x k>=x都是一个合法的解
因为num1 = num2 => (num1 + num2) % 2 == 0 可以构造一层的满覆盖
所以可以二分x 然后用网络流check

建图:
如果点k为白
建边(s, k, x – v[k])
如果为黑
建边(k, t, x – v[k])
对相邻点u、v (u为白)
建边 (u, v, inf)

 

转自http://hzwer.com/5992.html

黄学长写的真好orz

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define inf 1e15
#define maxn 10005
using namespace std;
int Te,n,m,num1,num2;
int head[maxn],cur[maxn],S,T,tot,flag[maxn];
long long s[45][45],sum1,sum2,Max,l,r,d[maxn];
struct node{
    int v,nex;
    long long cap;
}e[200005];
int get(int i,int j){
    return (i-1)*m+j;
}
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
bool can(int i,int j){
    if(i>0&&i<=n&&j>0&&j<=m)return 1;
    return 0;
}
void lj(int t1,int t2,long long t3){
    e[++tot].v=t2;e[tot].cap=t3;e[tot].nex=head[t1];head[t1]=tot;
    e[++tot].v=t1;e[tot].cap=0;e[tot].nex=head[t2];head[t2]=tot;
}
bool BFS(){
    for(int i=1;i<=T;i++)d[i]=inf;
    queue<int>q;q.push(S);d[S]=0;
    while(!q.empty()){
        int x=q.front();q.pop();cur[x]=head[x];
        for(int i=head[x];i;i=e[i].nex){
            if(d[e[i].v]>d[x]+1&&e[i].cap){
                d[e[i].v]=d[x]+1;
                if(!flag[e[i].v]){
                    q.push(e[i].v);flag[e[i].v]=1;
                }
            }
        }
        flag[x]=0;
    }
    return d[T]!=inf;
}
long long lian(int k,long long a){
    if(k==T||!a)return a;
    long long f,flow=0;
    for(int &i=cur[k];i;i=e[i].nex){
        if(d[e[i].v]==d[k]+1&&(f=lian(e[i].v,min(e[i].cap,a)))>0){
            flow+=f;a-=f;
            e[i].cap-=f;e[i^1].cap+=f;
            if(!a)break;
        }
    }
    return flow;
}
bool pd(long long mid){
    S=10000,T=10001;tot=1;
    long long cnt=0,ans=0;
    for(int i=1;i<=T;i++)head[i]=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            int now=get(i,j);
            if(mid<s[i][j])return 0;
            if((i+j)&1){
                lj(S,now,mid-s[i][j]);cnt+=mid-s[i][j];
                for(int t=0;t<4;t++){
                    int tx=i+dx[t],ty=j+dy[t];
                    if(can(tx,ty))lj(now,get(tx,ty),inf);
                }
            }
            else {
                lj(now,T,mid-s[i][j]);
            }
        }
    }
    while(BFS())ans+=lian(S,inf);
    return ans==cnt;
}
int main(){
    cin>>Te;
    while(Te--){
        scanf("%d%d",&n,&m);
        num1=num2=0;sum1=sum2=0;Max=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                scanf("%lld",&s[i][j]);
                Max=max(Max,s[i][j]);
                if((i+j)&1){
                    num1++,sum1+=s[i][j];
                }
                else num2++,sum2+=s[i][j];
            }
        }
        if(num1==num2){
            l=Max,r=inf;
            while(l<r){
                int mid=l+r>>1;
                if(pd(mid))r=mid;
                else l=mid+1;
            }
            cout<<num1*r-sum1<<endl;
        }
        else {
            long long x=(sum1-sum2)/(num1-num2);
            //cout<<x<<sum1<<' '<<sum2<<' '<<num1<<' '<<num2<<endl;
            if(pd(x))cout<<num1*x-sum1<<endl;
            else puts("-1");
        }
    }
    return 0;
}
posted @ 2018-08-12 16:17  liankewei123456  阅读(185)  评论(0编辑  收藏  举报