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
22 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 奇怪的遊戲 最大流