zoukankan      html  css  js  c++  java
  • 交换棋子[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 }
  • 相关阅读:
    监控系统播放音频断断续续的原因
    apachelog4cxx编译
    [架构]FileZilla Server架构分析
    MAC上命令行内容传递到剪贴板
    IOS上屏蔽SIGPIPE
    我想要这样的3g无线路由器
    【转】MySQL日期时间函数大全
    svnplot 将svn的日志导入数据库
    Foxmail for mac 1.0.0 使用小评
    Versions 升级了,不需要把命令行svn退到1.6了,哇咔咔
  • 原文地址:https://www.cnblogs.com/zub23333/p/8849559.html
Copyright © 2011-2022 走看看