交换棋子[CQOI2012]

                                          题目传送门

 

题目描述

有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态。要求第i行第j列的格子只能参与mi,j次交换。

输入输出格式

输入格式:

 

第一行包含两个整数n,m(1<=n, m<=20)。以下n行为初始状态,每行为一个包含m个字符的01串,其中0表示黑色棋子,1表示白色棋子。以下n行为目标状态,格式同初始状态。以下n行每行为一个包含m个0~9数字的字符串,表示每个格子参与交换的次数上限。

 

输出格式:

 

输出仅一行,为最小交换总次数。如果无解,输出-1。

 

输入输出样例

输入样例#1: 
3 3
110
000
001
000
110
100
222
222
222
输出样例#1: 
4

  格子的交换其实可以看做黑棋的移动。所以我们可以考虑拆点做费用流。
   我一开始的思路:
  • 把每个点拆成两个点xi,yi;
  • xi->yi连容量为该点交换次数,费用为0的边;
  • yi->xj连容量为INF,费用为1的边;(i与j有公共边或公共点)
  • s->xi连容量为1,费用为0的边;(格子i开始时为黑棋)
  • yi->t连容量为1,费用为0的边;(格子i结束时为黑棋)

 

  可是这样建图是不对的。考虑一条移动的路径,起点和终点翻转了一次,路径上的其余点其实反转了两次,那我们怎么解决这个问题呢?

  我们先不考虑起点和终点,那么路径上的格子其实每经过一个棋子就要被翻转两次,所以我们让通过次数除以2就可以了。再考虑起点和终点,如果一个格子既是起点又是终点,显然可以不用管它,当做这里没有棋子处理即可。对于一个是起点或终点的格子,我们可以先给他留出一个初始通过的交换次数,即让以该格子为起点的棋子离开,然后剩下的就按照这个格子是路径上的点处理即可,即容量为(w-1)/2+1。

 

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<queue>
  6 #include<bitset>
  7 #define dx(i,j) ((i-1)*m+j)
  8 #define dy(i,j) ((i-1)*m+j+400)
  9 using namespace std ;
 10 const int INF = 0x7ffffff ;
 11 const int N = 20 + 2 ;
 12 const int FN = 800 + 10 ;
 13 const int M = 1e6 + 10 ;
 14 
 15 const int cx[] = {-1,-1,-1,0,0,1,1,1} ;
 16 const int cy[] = {1,0,-1,1,-1,1,0,-1} ;
 17 
 18 inline int read() {
 19     int k = 0, f = 1 ; char c = getchar() ;
 20     for( ; !isdigit(c) ; c = getchar())
 21       if(c == '-') f = -1 ;
 22     for( ; isdigit(c) ; c = getchar())
 23       k = k*10 + c-'0' ;
 24     return k*f ;
 25 }
 26 struct Edge {
 27     int to, next, flow, cost ;
 28 }e[M] ;
 29 int n, m, s, t, ansf, ansc ; int head[FN], dis[FN] ; bool hh[N][N], gg[N][N], vis[FN] ;
 30 inline void add_edge(int x,int y,int ff,int cc) {
 31     static int cnt = 1 ;
 32     e[++cnt].to = y, e[cnt].next = head[x], head[x] = cnt, e[cnt].flow = ff, e[cnt].cost = cc ;
 33     e[++cnt].to = x, e[cnt].next = head[y], head[y] = cnt, e[cnt].flow = 0, e[cnt].cost = -cc ;
 34 }
 35 
 36 inline bool spfa() {
 37     for(int i=1;i<=t;i++) dis[i] = INF ; dis[s] = 0 ;
 38     deque<int>q ; q.push_back(s) ; bitset<FN>inq ; inq[s] = 1 ;
 39     while(!q.empty()) {
 40         int x = q.front() ; q.pop_front() ; inq[x] = 0 ;
 41         for(int i=head[x];i;i=e[i].next) {
 42             int y = e[i].to ; if(!e[i].flow) continue ;
 43             if(dis[y] > dis[x]+e[i].cost) {
 44                 dis[y] = dis[x]+e[i].cost ;
 45                 if(!inq[y]) {
 46                     inq[y] = 1 ;
 47                     if(!q.empty() && dis[y] < dis[q.front()]) q.push_front(y) ;
 48                     else q.push_back(y) ;
 49                 }
 50             }
 51         }
 52     }
 53     return dis[t] < INF ;
 54 }
 55 int FFdfs(int x,int minflow) {
 56     vis[x] = 1 ;
 57     if(x == t || !minflow) return minflow ;
 58     int fflow = 0 ;
 59     for(int i=head[x];i;i=e[i].next) {
 60         int y = e[i].to ; if(!e[i].flow || vis[y] || dis[y] != dis[x]+e[i].cost) continue ;
 61         int temp = FFdfs(y,min(minflow,e[i].flow)) ;
 62         fflow += temp, minflow -= temp ;
 63         e[i].flow -= temp, e[i^1].flow += temp ;
 64         ansc += temp*e[i].cost ;
 65         if(!minflow) break ;
 66     }
 67     return fflow ;
 68 }
 69 
 70 int main() {
 71     n = read(), m = read() ; s = 801, t = s+1 ; int tot = 0, tot1 = 0 ;
 72     for(int i=1;i<=n;i++) 
 73       for(int j=1;j<=m;j++) {
 74            char cc ; cin>>cc ; 
 75            if(cc == '1') {
 76                hh[i][j] = 1, tot1++ ;
 77          }
 78       }
 79     for(int i=1;i<=n;i++) 
 80       for(int j=1;j<=m;j++) {
 81            char cc ; cin>>cc ;
 82            if(cc == '1') {
 83                gg[i][j] = 1, tot++ ;
 84          }
 85       }
 86     while(tot != tot1) {
 87         printf("-1") ; return 0 ;
 88     } tot = 0 ;
 89     for(int i=1;i<=n;i++)
 90       for(int j=1;j<=m;j++) {
 91            if(hh[i][j] && !gg[i][j]) add_edge(s,dx(i,j),1,0), tot++ ;
 92          else if(!hh[i][j] && gg[i][j]) add_edge(dy(i,j),t,1,0) ; 
 93       }
 94     for(int i=1;i<=n;i++) 
 95       for(int j=1;j<=m;j++) {
 96            char cc ; cin>>cc ; int x = cc-'0' ; 
 97            add_edge(dx(i,j),dy(i,j),x>>1,0) ;
 98            if(hh[i][j] != gg[i][j] && x&1) add_edge(dx(i,j),dy(i,j),1,0) ;
 99            for(int k=0;k<8;k++) {
100                int xx = i+cx[k], yy = j+cy[k] ; if(!xx || !yy || xx > n || yy > m) continue ;
101                add_edge(dy(i,j),dx(xx,yy),INF,1) ;
102          }
103       }
104     while(spfa()) {
105         vis[t] = 1 ;
106         while(vis[t]) {
107             memset(vis,0,sizeof(vis)) ;
108             ansf += FFdfs(s,INF) ;
109          }
110     } 
111     if(ansf < tot) {
112         printf("-1") ; return 0 ;
113     }
114     printf("%d",ansc) ;
115     return 0 ;
116 }

 

 

 

 

posted @ 2018-04-16 07:16  zubizakeli  阅读(378)  评论(0编辑  收藏  举报