1. 程式人生 > >BZOJ 2756 SCOI2012 奇怪的遊戲 最大流

BZOJ 2756 SCOI2012 奇怪的遊戲 最大流

小結 link bsp std 黑點 配對 int long AD

題目鏈接: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 }

BZOJ 2756 SCOI2012 奇怪的遊戲 最大流