BZOJ 2756 SCOI2012 奇怪的游戏 最大流

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2756

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

————————————————————————————————————————————————————————

题意概述:
给出一个N*M的棋盘,每个格子有一个数,每次可以选择两个相邻的格子都+1。
问最少操作多少次可以让所有的数变得一样,如果无解输出-1。

分析:
发现只知道操作次数并没有什么用(因为你也不知道要怎么去填)。
假设最后的格子里的数是x。
每次操作对相邻的两个格子进行,发现可以把棋盘上的格子分开来,黑白染色。

抽象化表达:
假设有c1个白色格子,c2个黑色格子,一开始白色格子的和为s1,黑色格子的和为s2,那么假如答案可以成立,由分别对于黑白格子操作次数相同,有:
c1*x-s1=c2*x-s2 -> (c1-c2)*x=s1-s2

可以发现当c1=c2的时候x的值并不是唯一确定的,但是根据黑白染色的分析,可以发现这种情况下棋盘长宽中至少有一个是偶数,黑白可以两两配对,满足二分性质。
问题转化为判定。
建立源点S,汇点T,S向所有的白格子连边,容量为需要提升的值,黑格子向T连边,容量也为需要提升的值。
白点向周围的黑点连边,意义为这两个点一起提升的值,容量为inf。跑最大流看是否满流即可。

c1-c2!=0 -> x=(s1-s2)/(c1-c2),那么可以直接判定:是否大于等于最大格子,是否可以整除,是否可以判定成功。

小结:性质分析不出来怎么办?抽象成数学表达式再分析aaaaaaa!!!!

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<cstdlib>
  5 #include<algorithm>
  6 #include<queue>
  7 #include<set>
  8 #include<map>
  9 #include<vector>
 10 #include<cctype>
 11 #define inf (1e12+5)
 12 using namespace std;
 13 const int MAXN=45;
 14 typedef long long LL;
 15
 16 int T,N,M,A[MAXN][MAXN];
 17 int c1,c2,MAX; LL s1,s2;
 18 struct NET{
 19     static const int maxn=1605;
 20     static const int maxm=10005;
 21     struct edge{ int from,to,next; LL cap,flow; }E[maxm];
 22     int n,S,T,first[maxn],np,d[maxn],gap[maxn],fl[maxn],cur[maxn];
 23     NET(){ np=0; }
 24     void add_edge(int u,int v,LL c){
 25         E[++np]=(edge){u,v,first[u],c,0};
 26         first[u]=np;
 27         E[++np]=(edge){v,u,first[v],0,0};
 28         first[v]=np;
 29     }
 30     int id(int x,int y){ return (x-1)*M+y; }
 31     void init(LL m){
 32         memset(first,0,sizeof(first));
 33         np=0,n=N*M+2,S=n-1,T=n;
 34         LL re=0;
 35         for(int i=1;i<=N;i++)
 36         for(int j=1;j<=M;j++){
 37             if((i&1)&&(j&1)||!(i&1)&&!(j&1)){
 38                 add_edge(S,id(i,j),m-A[i][j]);
 39                 if(i-1) add_edge(id(i,j),id(i-1,j),inf);
 40                 if(j-1) add_edge(id(i,j),id(i,j-1),inf);
 41                 if(i+1<=N) add_edge(id(i,j),id(i+1,j),inf);
 42                 if(j+1<=M) add_edge(id(i,j),id(i,j+1),inf);
 43             }
 44             else add_edge(id(i,j),T,m-A[i][j]);
 45         }
 46     }
 47     void BFS(){
 48         queue<int>q;
 49         for(int i=1;i<=n;i++) d[i]=n;
 50         d[T]=0; q.push(T);
 51         while(!q.empty()){
 52             int i=q.front(); q.pop();
 53             for(int p=first[i];p;p=E[p].next){
 54                 int j=E[p].to,pp=(p-1^1)+1;
 55                 if(d[j]==n&&E[pp].cap>E[pp].flow) d[j]=d[i]+1,q.push(j);
 56             }
 57         }
 58     }
 59     LL augment(){
 60         LL flow=inf; int now=T;
 61         while(now!=S){
 62             flow=min(flow,E[fl[now]].cap-E[fl[now]].flow);
 63             now=E[fl[now]].from;
 64         }
 65         now=T;
 66         while(now!=S){
 67             E[fl[now]].flow+=flow,E[(fl[now]-1^1)+1].flow-=flow;
 68             now=E[fl[now]].from;
 69         }
 70         return flow;
 71     }
 72     bool ISAP(){
 73         memcpy(cur,first,sizeof(first));
 74         memset(gap,0,sizeof(gap));
 75         BFS();
 76         for(int i=1;i<=n;i++) gap[d[i]]++;
 77         int now=S; LL flow=0;
 78         while(d[S]<n){
 79             if(now==T) flow+=augment(),now=S;
 80             bool ok=0;
 81             for(int p=cur[now];p;p=E[p].next){
 82                 int j=E[p].to;
 83                 if(E[p].cap>E[p].flow&&d[j]+1==d[now]){
 84                     ok=1,cur[now]=fl[j]=p,now=j;
 85                     break;
 86                 }
 87             }
 88             if(!ok){
 89                 int minl=n;
 90                 for(int p=first[now];p;p=E[p].next){
 91                     int j=E[p].to;
 92                     if(E[p].cap>E[p].flow&&d[j]+1<minl) minl=d[j]+1;
 93                 }
 94                 if(--gap[d[now]]==0) break;
 95                 gap[d[now]=minl]++;
 96                 cur[now]=first[now];
 97                 if(now!=S) now=E[fl[now]].from;
 98             }
 99         }
100         for(int p=first[S];p;p=E[p].next)
101             if(E[p].cap!=E[p].flow) return 0;
102         for(int p=first[T],pp=(p-1^1)+1;p;p=E[p].next,pp=(p-1^1)+1)
103             if(E[pp].cap!=E[pp].flow) return 0;
104         return 1;
105     }
106 }net;
107
108 void data_in()
109 {
110     scanf("%d%d",&N,&M);
111     for(int i=1;i<=N;i++)
112     for(int j=1;j<=M;j++)
113         scanf("%d",&A[i][j]);
114     c1=c2=MAX=0,s1=s2=0;
115     for(int i=1;i<=N;i++)
116     for(int j=1;j<=M;j++){
117         if((i&1)&&(j&1)||!(i&1)&&!(j&1)) s1+=A[i][j],c1++;
118         else s2+=A[i][j],c2++;
119         MAX=max(MAX,A[i][j]);
120     }
121 }
122 bool check(LL mid)
123 {
124     net.init(mid);
125     return net.ISAP();
126 }
127 void work()
128 {
129     if(c1==c2){
130         LL L=MAX,R=inf,mid,ans=-1;
131         while(L<R){
132             mid=L+R>>1;
133             if(check(mid)) R=mid,ans=mid;
134             else L=mid+1;
135         }
136         if(ans!=-1) cout<<(ans*N*M-s1-s2)/2<<‘\n‘;
137         else cout<<ans<<‘\n‘;
138     }
139     else{
140         if((s1-s2)%(c1-c2)==0&&(s1-s2)/(c1-c2)>=MAX){
141             LL ans=(s1-s2)/(c1-c2);
142             if(check(ans)) cout<<(ans*N*M-s1-s2)/2<<‘\n‘;
143             else cout<<-1<<‘\n‘;
144         }
145         else cout<<-1<<‘\n‘;
146     }
147 }
148 int main()
149 {
150     scanf("%d",&T);
151     while(T--){
152         data_in();
153         work();
154     }
155     return 0;
156 }

原文地址:https://www.cnblogs.com/KKKorange/p/8597724.html

时间: 2024-12-16 05:13:52

BZOJ 2756 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 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成

BZOJ 2756 SCOI 奇怪的游戏

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

【BZOJ-2756】奇怪的游戏 最大流 + 分类讨论

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

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

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

【BZOJ2756】【SCOI2012】奇怪的游戏 最大流、

题解: 首先我并不知道这个怎么才能想出来正解. 只能说以后遇到这种题就往黑白染色以及大体关系上靠靠了. 好了,说这道题题解. 首先要相邻格子黑白染色,然后发现每次加权都会让黑白集合总权值各+1. 我们设最终所有格子的权值都为x 这个时候我们就可以讨论, 如果黑色格子和白色格子的数量不同,那么最后就可以发现当所有格子都为x时,两个集合的总权值差就是x(设cnta为黑色格子个数,cntb为白色,那么差就是|(cnta-cntb)*x|),而权值差是不变的,所以O(1)算出x值,然后check是否合法

BZOJ2756: [SCOI2012]奇怪的游戏

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2756 对棋盘进行黑白染色.若答案为x,有c1*x-s1==c2*x-s2得x=(s1-s2)/(c1-c2),那么若c1!=c2,检测x就可以了.若不是就二分x. 建图:s->黑点 c=x-a[i][j] 白点->t c=x-a[i][j] 黑->白 c=inf #include<cstring> #include<iostream> #include<

SCOI2012 奇怪的游戏

题目链接:戳我 怎么说呢,看到棋盘(应该想到二分图染色) 设白色格子数量为\(cnt0\),现在的值的和为\(sum0\).黑色格子的数量为\(cnt1\),现在的值的和为\(sum1\),最后的答案为x. \(sum0+cnt0*x=sum1+cnt1*x\) \(sum0-sum1=x*(cnt1-cnt0)\) 如果\(cnt1-cnt0\)不为0的时候,我们可以直接获得答案,然后验证答案的正确性. 我们不容易知道到底最后的终止数为多少,但是我们知道如果格子数量是偶数的话,如果一个x成立,