刷题总结——奇怪的游戏(scoi2012)

题目:

题目描述

Blinker 最近喜欢上一个奇怪的游戏。
这个游戏在一个 N*M  的棋盘上玩,每个格子有一个数。每次 Blinker  会选择两个相邻的格子,并使这两个数都加上 1。
现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成同一个数则输出 -1。

输入格式

输入的第一行是一个整数 T,表示输入数据有T 轮游戏组成。
每轮游戏的第一行有两个整数 N 和 M , 分别代表棋盘的行数和列数。
接下来有 N 行,每行 M 个数。 

输出格式

对于每个游戏输出最少能使游戏结束的次数,如果永远不能变成同一个数则输出 -1。

样例数据 1

输入  [复制]

 

 


2 2 
1 2 
2 3 
3 3 
1 2 3 
2 3 4 
4 3 2

输出


-1

备注

 【数据范围】
对于 30% 的数据,保证 T<=10,1<=N,M<=8
对于 100% 的数据,保证 T<= 10,1<=N,M<=40,所有数为正整数且小于 1000000000 

题解:

  这里引用http://www.cnblogs.com/DaD3zZ-Beyonder/p/5765882.html:

  一道比较有趣的题目

  先对题目进行分析:

  首先我们考虑对棋盘黑白染色,那么我们发现:“每次相邻两个+1”,显然是一黑一白+1

  那么我们先统计出WhiteNum,BlackNum(黑白点的数目),WhiteSum,BlackSum(黑白点初始权值和)(接下来可能用Wn,Ws,Bn,Bs代替)

  那么对于一次增加,显然是WhiteSum+1,BlackSum+1

  考虑对最后的情况进行讨论:

  那么很显然,当WhiteNum==BlackNum时(即总点数为偶数)

  如果WhiteSum!=BlackSum,显然无解

  如果WhiteSum==BlackSum时,我们发现,对于X如果成立,那么X+1一定成立,显然满足二分的性质,那么二分这个值,进行判定

  当WhiteNum!=BlackNum时(即总点数为奇数)

  发现显然,若有解,则解唯一,那么直接验证正确性即可

  至于解怎么求?

  设最后所有数变为X,易得X*Wn-Ws=X*Bn-Bs,整理下可得:X=(Ws-Bs)/(Wn-Bn),用网络流验证即可

  那么考虑建图:

  S-->白点,约束为X-val[i][j]

  黑点-->T,约束为X-val[i][j]

  相邻的白点-->黑点,约束为INF

  判断是否满流即可

  最后说下个人心得:

  这道题的重点其实不是网络流,而是黑白染色这一想法,由相邻格子染色其实可以考虑到这一点,以后做到相似的题要注意这一想法····然后就是后面的分类讨论···也是由黑白染色后考虑答案的性质得出的·····这是一道很好的题,染色+二分+网络流;

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int N=2010;
const int M=1001000;
const long long inf=1e+18;
int Ri()
{
  char c; 
  int f=0;
  for(c=getchar();c<'0'||c>'9';c=getchar());
  for(;c<='9'&&c>='0';c=getchar())
    f=(f<<3)+(f<<1)+c-'0';
  return f;
}
long long Rl()
{
  char c; 
  long long f=0;
  for(c=getchar();c<'0'||c>'9';c=getchar());
  for(;c<='9'&&c>='0';c=getchar())
    f=f*10+c-'0';
  return f;
}
int T,n,m,tot,first[N],cur[N],lev[N],next[M],go[M],Wn,Bn,src,des;
int color[N][N],num[N][N];
long long map[N][N],rest[M],Ws,Bs,ans,sum;
inline void comb(int a,int b,long long c)
{
  next[++tot]=first[a],first[a]=tot,go[tot]=b,rest[tot]=c;
  next[++tot]=first[b],first[b]=tot,go[tot]=a,rest[tot]=0;
}
inline bool bfs()
{
  for(int i=src;i<=des;i++)  cur[i]=first[i],lev[i]=-1;
  static int que[N],tail,u,v;
  que[tail=1]=src;
  lev[src]=0;
  for(int head=1;head<=tail;head++)
  {
    u=que[head];
    for(int e=first[u];e;e=next[e])
    {
      if(lev[v=go[e]]==-1&&rest[e])
      {
        lev[v]=lev[u]+1;
        que[++tail]=v;
        if(v==des)  return true;
      }
    }
  }
  return false;
}
inline long long dinic(int u,long long flow)
{
  if(u==des)
    return flow;
  long long res=0,delta;
  int v;
  for(int &e=cur[u];e;e=next[e])
  {
    if(lev[v=go[e]]>lev[u]&&rest[e])
    {
      delta=dinic(v,min(flow-res,rest[e]));
      if(delta)
      {
        rest[e]-=delta;
        rest[e^1]+=delta;
        res+=delta;
        if(res==flow)  break;
      }
    }
  }
  if(flow!=res)  lev[u]=-1;
  return res;
}
inline long long maxflow()
{
  long long temp=0;
  while(bfs())
    temp+=dinic(src,inf);
  return temp;
}
inline bool jud(int x,int y)
{
  return x>=1&&x<=n&&y>=1&&y<=m;
}
inline bool check(long long lim)
{
  tot=1;
  memset(first,0,sizeof(first));
  for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {  
      if(
      !color[i][j])
        comb(src,num[i][j],lim-map[i][j]);
      else
        comb(num[i][j],des,lim-map[i][j]);
    }
  for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
      if(!color[i][j])
      {
        if(jud(i-1,j))
          comb(num[i][j],num[i-1][j],inf);
        if(jud(i+1,j))
          comb(num[i][j],num[i+1][j],inf);
        if(jud(i,j-1))
          comb(num[i][j],num[i][j-1],inf);
        if(jud(i,j+1))
          comb(num[i][j],num[i][j+1],inf);
      }
  long long temp=maxflow();
  if(temp==((long long)Wn*lim-Ws))
    return true;
  else return false;
}
inline void solve()
{
  Wn=Ws=Bn=Bs=0;
  src=0;
  des=n*m+1;
  int cnt=0;
  long long left=0,right=1e+18;
  for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {  
      num[i][j]=++cnt;
      color[i][j]=(i+j)%2;
      if(!color[i][j])
        Wn++,Ws+=map[i][j];
      else
        Bn++,Bs+=map[i][j];
      left=max(left,map[i][j]);
    }
  if(Wn==Bn)
  {
    if(Ws!=Bs)  
    {
      cout<<"-1"<<endl;
      return;
    }
    else
    {
      while(left<=right)
      {
        long long mid=(left+right)/2;
        if(check(mid))  ans=mid,right=mid-1;
        else left=mid+1;
      }
      cout<<ans*Wn-Ws<<endl;
    }
  }
  else
  {
    ans=(long long)(Ws-Bs)/(Wn-Bn);
    if(ans<left)
    {
      cout<<"-1"<<endl;
      return;
    }
    
    if(check(ans))  
    {
      cout<<ans*Wn-Ws<<endl;
      return;
    }
    else  cout<<"-1"<<endl;
  }
}
int main()
{
 // freopen("a.in","r",stdin);
  T=Ri();
  while(T--)
  {
    n=Ri(),m=Ri();
    for(int i=1;i<=n;i++)
      for(int j=1;j<=m;j++)
        map[i][j]=Rl();
    solve();
  }
  return 0;
}

 

posted @ 2017-09-01 20:09  AseanA  阅读(269)  评论(0编辑  收藏  举报