题目链接: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
1 2
2 3
3 3
1 2 3
2 3 4
4 3 2
Sample Output
-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 }