• P3159 [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





    好像所有的没有思路的题都能用毒瘤的网络流来搞一下,
    这题真的毒瘤,第一次见拆三个点的
    看到一个点最多参与几次交换,就应该想到拆点限流,然后最后问你最先的代价,应该可以联想到费用这一块,
    那就向费用流这块搞啊,这题的建模是真的难想,所以说就不详细写了,细节挺多的。
    然后就是不用担心一个1到达目标的时候,打乱的别的1,因为这时通过交换两个点的先后顺序来避免这个问题。
    放个网址: https://www.luogu.org/problemnew/solution/P3159



      1 #include<queue>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<iostream>
      5 #include<algorithm>
      6 #define MAXN 2010
      7 #define MAXM 64010 
      8 #define INF 0x3f3f3f3f
      9 #define fpop(x) x.front();x.pop()
     10 using namespace std;
     11 
     12 int pre_node[MAXN],pre_edge[MAXN];
     13 
     14 char ch,mp_bg[25][25],mp_ed[25][25];
     15 
     16 int n,m,cnt=-1,dis[MAXN],vis[MAXN],flow[MAXN],head[MAXN],maxf[25][25];
     17 
     18 struct edge{
     19     int nxt,to,w,f;
     20 }e[MAXM];
     21 
     22 inline int _bg(int x,int y){return n*m*0+(x-1)*m+y;}//起始点[x,y]的编号
     23 inline int _ed(int x,int y){return n*m*1+(x-1)*m+y;}//目标点[x,y]的编号 
     24 inline int _inn(int x,int y){return n*m*2+(x-1)*m+y;}//棋盘[x,y]的Inn点编号 
     25 inline int _mid(int x,int y){return n*m*3+(x-1)*m+y;}//棋盘[x,y]的mid点标号
     26 inline int _out(int x,int y){return n*m*4+(x-1)*m+y;}//棋盘[x,y]的Out点编号 
     27 
     28 inline bool in_map(int x,int y){
     29     return 1<=x && x<=n && 1<=y && y<=m;
     30 }//判断是否越界 
     31 
     32 inline void add_edge(int from,int to,int flw,int val){
     33     e[++cnt].nxt=head[from];
     34     e[cnt].to=to;
     35     e[cnt].f=flw;
     36     e[cnt].w=val;
     37     head[from]=cnt;
     38 }
     39 
     40 queue<int>que;
     41 
     42 inline bool spfa(int s,int t){
     43     memset(vis,0,sizeof(vis));
     44     memset(dis,0x3f,sizeof(dis));
     45     memset(flow,0x3f,sizeof(flow));
     46     que.push(s); vis[s]=true; dis[s]=0;
     47     while(!que.empty()){
     48         int u=fpop(que);
     49         for(int i=head[u];~i;i=e[i].nxt){
     50             int v=e[i].to;
     51             if(dis[v]>dis[u]+e[i].w && e[i].f){
     52                 dis[v]=dis[u]+e[i].w;
     53                 flow[v]=min(flow[u],e[i].f);
     54                 pre_node[v]=u;
     55                 pre_edge[v]=i;
     56                 vis[v]=true;que.push(v);
     57             }
     58         }
     59     }
     60     return dis[t]!=INF;
     61 }
     62 
     63 int mv[8][2]={{1,0},{-1,0},{0,1},{0,-1},{1,1},{1,-1},{-1,1},{-1,-1}};
     64 
     65 int main(){
     66     memset(head,-1,sizeof(head));
     67     scanf("%d%d",&n,&m);
     68     for(int i=1;i<=n;++i){
     69         for(int j=1;j<=m;++j){
     70             scanf(" %c",&mp_bg[i][j]);
     71         }
     72     }
     73     for(int i=1;i<=n;++i){
     74         for(int j=1;j<=m;++j){
     75             scanf(" %c",&mp_ed[i][j]);
     76         }
     77     }
     78     for(int i=1;i<=n;++i){
     79         for(int j=1;j<=m;++j){
     80             scanf(" %c",&ch);
     81             maxf[i][j]=ch-'0';
     82             //最大经过次数 
     83         }
     84     }
     85     //输入起始态和目标态棋盘
     86     int s=0,t=n*m*5+1;
     87     int cnt_1=0,cnt_2=0;
     88     for(int i=1;i<=n;++i){
     89         for(int j=1;j<=m;++j){
     90             if(mp_bg[i][j]==mp_ed[i][j]){
     91                 add_edge(_inn(i,j),_mid(i,j),maxf[i][j]/2,0);
     92                 add_edge(_mid(i,j),_inn(i,j),000000000000,0);
     93 
     94                 add_edge(_mid(i,j),_out(i,j),maxf[i][j]/2,0);
     95                 add_edge(_out(i,j),_mid(i,j),000000000000,0);
     96             }else{
     97                 if(mp_bg[i][j]=='1'){
     98                     add_edge(_inn(i,j),_mid(i,j),(maxf[i][j]+0)/2,0);
     99                     add_edge(_mid(i,j),_inn(i,j),000000000000,0);
    100 
    101                     add_edge(_mid(i,j),_out(i,j),(maxf[i][j]+1)/2,0);
    102                     add_edge(_out(i,j),_mid(i,j),000000000000,0);
    103                 }
    104                 if(mp_ed[i][j]=='1'){
    105                     add_edge(_inn(i,j),_mid(i,j),(maxf[i][j]+1)/2,0);
    106                     add_edge(_mid(i,j),_inn(i,j),000000000000,0);
    107 
    108                     add_edge(_mid(i,j),_out(i,j),(maxf[i][j]+0)/2,0);
    109                     add_edge(_out(i,j),_mid(i,j),000000000000,0);
    110                 }
    111             }
    112 
    113             if(mp_bg[i][j]=='1'){
    114                 ++cnt_1;
    115                 //连接源点到初始点     f=1 w=0;
    116                 add_edge(s,_mid(i,j),1,0);
    117                 add_edge(_mid(i,j),s,0,0);
    118                 //连接起始点到棋盘 
    119               //  add_edge(_bg(i,j),_mid(i,j),1,0);
    120               //  add_edge(_mid(i,j),_bg(i,j),0,0);
    121             }
    122             if(mp_ed[i][j]=='1'){
    123                 ++cnt_2;
    124                 //连接终结点到汇点     f=1 w=0; 
    125                 add_edge(_mid(i,j),t,1,0);
    126                 add_edge(t,_mid(i,j),0,0);
    127                 //连接棋盘到终结点 
    128               //  add_edge(_mid(i,j),_ed(i,j),1,0);
    129                // add_edge(_ed(i,j),_mid(i,j),0,0);
    130             }
    131             //棋盘的八连通边  f=INF w=1;
    132             for(int k=0;k<8;++k){
    133                 int ni=i+mv[k][0];
    134                 int nj=j+mv[k][1];
    135                 if(in_map(ni,nj)){
    136                     //从点[i,j]的out连接点[ni,nj]的inn 
    137                     add_edge(_out(i,j),_inn(ni,nj),INF,+1);
    138                     add_edge(_inn(ni,nj),_out(i,j),000,-1);
    139                 }
    140             }
    141         }
    142     }
    143     //棋子数变动->No solution 
    144     if(cnt_1!=cnt_2){
    145         puts("-1");
    146         return 0;
    147     }
    148     //然后跑费用流 
    149     int max_flow=0,min_cost=0;
    150     while(spfa(s,t)){
    151         max_flow+=flow[t];
    152         min_cost+=flow[t]*dis[t];
    153         int u=t;
    154         while(u!=s){
    155             e[pre_edge[u]^0].f-=flow[t];
    156             e[pre_edge[u]^1].f+=flow[t];
    157             u=pre_node[u];
    158         }
    159     }
    160     if(max_flow!=cnt_1){
    161         puts("-1");
    162         return 0;
    163     }
    164     printf("%d
    ",min_cost);
    165 }



  • 相关阅读:
    Jenkins的插件管理(安装和更新插件)
    [Flutter] MacOS/Windows Flutter 环境走一遍
    [Sw] 使用 Swoole Server task/协程 处理大数据量异步任务时注意
    [Sw] Swoole-4.2.9 可以尝试愉快应用 Swoole 协程
    [PHP] 常备的现代 PHP 项目开发准备
    [SF] Symfony 标准 HttpFoundationRequest 实现分析
    [Linux] umask 从三类人群的权限中拿走权限数字
    [Design] 后端程序的高并发与异步
    [Linux]系统管理: 进程管理(ps/top/pstree/kill/pkill), 工作管理, 系统资源查看, 系统定时任务
    [FE] 有效开展一个前端项目-V2 (vuejs-templates/webpack)
  • 原文地址:https://www.cnblogs.com/zhangbuang/p/10638102.html
Copyright © 2020-2023  润新知