刷题总结——奇怪的游戏(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;
}
时间: 2024-10-15 00:23:28

刷题总结——奇怪的游戏(scoi2012)的相关文章

BZOJ 2756: [SCOI2012]奇怪的游戏 [最大流 二分]

2756: [SCOI2012]奇怪的游戏 Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 3352  Solved: 919[Submit][Status][Discuss] Description Blinker最近喜欢上一个奇怪的游戏. 这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数.每次 Blinker 会选择两个相邻的格子,并使这两个数都加上 1. 现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成

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

2756: [SCOI2012]奇怪的游戏 Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 1594  Solved: 396[Submit][Status][Discuss] Description Blinker最近喜欢上一个奇怪的游戏. 这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数.每次 Blinker 会选择两个相邻 的格子,并使这两个数都加上 1. 现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变

BZOJ 2756: [SCOI2012]奇怪的游戏

2756: [SCOI2012]奇怪的游戏 Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 3410  Solved: 941[Submit][Status][Discuss] Description Blinker最近喜欢上一个奇怪的游戏. 这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数.每次 Blinker 会选择两个相邻的格子,并使这两个数都加上 1. 现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成

一次失败的刷题经历:[LeetCode]292之尼姆游戏(Nim Game)

最近闲来无事刷LeetCode,发现这道题的Accept Rate还是挺高的,尝试着做了一下,结果悲剧了,把过程写下来,希望能长点记性.该题的描述翻译成中文如下: 你正在和你的朋友玩尼姆游戏(Nim Game): 桌子上有一堆石块,你和你的朋友轮流去拿这些石块,每次只能拿1块.2块或者3块.在石块被拿光前,最后一次拿到石块的人获胜.你将首先去拿这些石块. 你和你的朋友都非常聪明,并且拥有应对该游戏的最佳策略.写一个函数来决定在给定石块数量的情况下,你是否能够获胜.比如:如果桌子上有4块石块,那么

LeetCode开心刷题五十一天——118. Pascal&#39;s Triangle 接触跳转表概念,不知用处 lamda逗号导致表达式加法奇怪不理解119. Pascal&#39;s Triangle II

118. Pascal's Triangle Easy 87984FavoriteShare Given a non-negative integer numRows, generate the first numRows of Pascal's triangle. In Pascal's triangle, each number is the sum of the two numbers directly above it. Example: Input: 5 Output: [ [1],

[SCOI2012]奇怪的游戏 (网络流)

Description Blinker最近喜欢上一个奇怪的游戏. 这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数.每次 Blinker 会选择两个相邻的格子,并使这两个数都加上 1. 现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成同一个数则输出-1. Input 输入的第一行是一个整数T,表示输入数据有T轮游戏组成. 每轮游戏的第一行有两个整数N和M, 分别代表棋盘的行数和列数. 接下来有N行,每行 M个数. Output 对于每个游戏输出最少能使游戏

BZOJ 2756 SCOI2012 奇怪的游戏 最大流

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2756 Description Blinker最近喜欢上一个奇怪的游戏. 这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数.每次 Blinker 会选择两个相邻的格子,并使这两个数都加上 1. 现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成同一个数则输出-1. Input 输入的第一行是一个整数T,表示输入数据有T轮游戏组成. 每轮游戏的第一行有两

编程日志&amp;&amp;刷题日志&amp;&amp;开发日志迁移之碎碎念

Given an array of integers, return indices of the two numbers such that they add up to a specific target. You may assume that each input would have exactly one solution, and you may not use the same element twice. Example: Given nums = [2, 7, 11, 15]

bzoj 2756奇怪的游戏

2756: [SCOI2012]奇怪的游戏 Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 2571  Solved: 685[Submit][Status][Discuss] Description Blinker最近喜欢上一个奇怪的游戏. 这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数.每次 Blinker 会选择两个相邻的格子,并使这两个数都加上 1. 现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成