2668: [cqoi2012]交换棋子

Time Limit: 3 Sec  Memory Limit: 128 MB
Submit: 1055  Solved: 388
[Submit][Status][Discuss]

Description

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

Input

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

Output

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

Sample Input

3 3
110
000
001
000
110
100
222
222
222

Sample Output

4

HINT

Source

Solution

这是一道建图比较有趣的费用流.

想到用费用流比较容易,很显然,连法必然是S连向原图中的黑点,新图中的黑点连向T,问题在于中间的连边,如何利用容量限制两个点。

自己一开始立马想到拆点,但是是很naive的一拆二,限制容量,但这样并不可以。

实际上这个题需要一个点拆成3个点,我们把点$x$拆成$x_{1},x_{2},x_{3}$,并连出$x_{1}-->x_{2}-->x_{3}$

其中$x_{1}-->x_{2}$表示$x$这个点最多流入的流量,$x_{2}-->x_{3}$表示$x$这个点最多流出的流量。

对于一个点$x$,如果只是原图的黑点,那么限制$<x_{1},x_{2}>:cap=\frac{use[x]}{2};cost=0 ; <x_{2},x_{3}>:cap=\frac{use[x]+1}{2};cost=0$

并且连$S-->x_{2} : cap=1;cost=0$

对于一个点$x$,如果只是新图的黑点,那么限制$<x_{1},x_{2}>:cap=\frac{use[x]+1}{2};cost=0 ; <x_{2},x_{3}>:cap=\frac{use[x]}{2};cost=0$

并且连$x_{2}-->T : cap=1;cost=0$

如果一个点$x$,如果在原图和新图中都是白点或者都是黑点,那么我们限制$<x_{1},x_{2}>:cap=\frac{use[x]}{2};cost=0 ; <x_{2},x_{3}>:cap=\frac{use[x]}{2};cost=0$

对于图中的八连通格两两$x ; y$,连边$x_{3}-->y_{1} :cap=INF ; cost=1$流经此边,表示交换一次。

上述的限制方法,实际上是对网格的每个位置的流量变化进行讨论得到的,因为是一个最值情况,所以很多流入流出限制都无法完全流满。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
#define MAXM 100010
#define MAXN 2000
int N,M,used[50][50];
char st[50][50],ed[50][50],us[50][50];
struct EdgeNode{int next,to,cap,cost;}edge[MAXM];
int cnt=1,head[MAXN];
inline void AddEdge(int u,int v,int w,int c) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v; edge[cnt].cap=w; edge[cnt].cost=c;}
inline void InsertEdge(int u,int v,int w,int c) {AddEdge(u,v,w,c); AddEdge(v,u,0,-c);}
int dis[MAXN],S,T,Cost,Flow;
bool mark[MAXN];
#define INF 0x7fffffff
inline bool SPFA()
{memset(mark,0,sizeof(mark));for (int i=S; i<=T; i++) dis[i]=INF;queue<int>q;q.push(S); dis[S]=0; mark[S]=1;while (!q.empty()){int now=q.front(); q.pop(); mark[now]=0;for (int i=head[now]; i; i=edge[i].next)if (edge[i].cap && dis[edge[i].to]>dis[now]+edge[i].cost){dis[edge[i].to]=dis[now]+edge[i].cost;if (!mark[edge[i].to]) q.push(edge[i].to),mark[edge[i].to]=1;}}return dis[T]!=INF;
}
inline int DFS(int loc,int low)
{mark[loc]=1;if (loc==T) return low;int used=0,w;for (int i=head[loc]; i; i=edge[i].next)if (edge[i].cap && !mark[edge[i].to] && dis[edge[i].to]==dis[loc]+edge[i].cost){w=DFS(edge[i].to,min(edge[i].cap,low-used));edge[i].cap-=w; edge[i^1].cap+=w; used+=w; Cost+=w*edge[i].cost;if (low==used) return used;}return used;
}
inline int zkw()
{int re=0;while (SPFA()){mark[T]=1;while (mark[T]) memset(mark,0,sizeof(mark)),re+=DFS(S,INF);}return re;
}
int id[50][50][4],ID,black,white;
int dx[10]={-1,-1,-1,0,0,1,1,1},dy[10]={-1,0,1,-1,1,-1,0,1};
inline bool check(int x,int y) {return x>=1 && x<=N && y>=1 && y<=M;}
void BuildGraph()
{for (int i=1; i<=N; i++)for (int j=1; j<=M; j++)for (int k=1; k<=3; k++)id[i][j][k]=++ID;S=0,T=ID+1;for (int i=1; i<=N; i++)for (int j=1; j<=M; j++){if (st[i][j]=='0' && ed[i][j]=='1')black++,InsertEdge(S,id[i][j][2],1,0),InsertEdge(id[i][j][1],id[i][j][2],used[i][j]/2,0),InsertEdge(id[i][j][2],id[i][j][3],(used[i][j]+1)/2,0);if (st[i][j]=='1' && ed[i][j]=='0')white++,InsertEdge(id[i][j][2],T,1,0),InsertEdge(id[i][j][1],id[i][j][2],(used[i][j]+1)/2,0),InsertEdge(id[i][j][2],id[i][j][3],used[i][j]/2,0);if (st[i][j]==ed[i][j])InsertEdge(id[i][j][1],id[i][j][2],used[i][j]/2,0),InsertEdge(id[i][j][2],id[i][j][3],used[i][j]/2,0);}for (int i=1; i<=N; i++)for (int j=1; j<=M; j++)for (int x,y,k=0; k<8; k++){x=i+dx[k],y=j+dy[k];if (check(x,y)) InsertEdge(id[i][j][3],id[x][y][1],INF,1);}
}
int main()
{scanf("%d%d",&N,&M);for (int i=1; i<=N; i++) scanf("%s",st[i]+1);for (int i=1; i<=N; i++) scanf("%s",ed[i]+1);for (int i=1; i<=N; i++) scanf("%s",us[i]+1);for (int i=1; i<=N; i++)for (int j=1; j<=M; j++) used[i][j]=us[i][j]-'0';BuildGraph();Flow=zkw();printf("%d\n",black==white? (Flow==black? Cost : -1) : -1);return 0;
} 

脑残RE了好几次...

转载于:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5962009.html

【BZOJ-2668】交换棋子 最小费用最大流相关推荐

  1. BZOJ 1070: [SCOI2007]修车(最小费用最大流)

    建图很神奇..建完图其实就是裸的费用流了.. -------------------------------------------------------------- #include<cs ...

  2. BZOJ 2597 剪刀石头布(最小费用最大流)(WC2007)

    Description 在一些一对一游戏的比赛(如下棋.乒乓球和羽毛球的单打)中,我们经常会遇到A胜过B,B胜过C而C又胜过A的有趣情况,不妨形象的称之为剪刀石头布情况.有的时候,无聊的人们会津津乐道 ...

  3. bzoj 1834: [ZJOI2010]network 网络扩容【最大流+最小费用最大流】

    第一问直接跑最大流即可.建图的时候按照费用流建,费用为0. 对于第二问,在第一问dinic剩下的残量网络上建图,对原图的每条边(i,j),建(i,j,inf,cij),表示可以用c的花费增广这条路.然 ...

  4. BZOJ 1221: [HNOI2001] 软件开发(最小费用最大流)

    不知道为什么这么慢.... 费用流,拆点.... --------------------------------------------------------------------------- ...

  5. bzoj 2245 [SDOI2011]工作安排【最小费用最大流】

    其实不用拆点,对于每个人我们假装他是\( s[i]+1 \)个点,可以由他向T点分别连\( s[i]+1 \)条边,容量为\( t[i][j]-t[i][j-1]\),由S点向所有产品i连容量为c[i ...

  6. bzoj 1070: [SCOI2007]修车【最小费用最大流】

    一开始从客人角度想的,怎么建都不对 从一个修车工所接待的所有顾客花费的总时间来看,设一共有x个人,那么第一个修的对总时间的贡献是x*w1,第二个是(x-1)*w2-以此类推.所以把第i个修车工拆成n组 ...

  7. 有下界的最小费用可行流2.0(bzoj 3876: [Ahoi2014]支线剧情)

    什么是有下界的最小费用可行流? 平时来讲都是最小费用最大流,也就是在满流的前提条件下费用尽可能的少,而最小费用可行流一般不要求满流,但是每条边都有最小流量要求(比如经过边e(u,v)的流量不能少于4等 ...

  8. BZOJ 1061 志愿者招募(最小费用最大流)

    题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1061 题意:申奥成功后,布布经过不懈努力,终于 成为奥组委下属公司人力资源部门的主管.布 ...

  9. 有上下限的最小费用可行流

    例题 BZOJ 3876 题意,有N个点,并且有一些边,每个点必须要经过一次,但是不限经过次数,也就是有下界的情况,且下界为1,现在每次的起点都是1,问至少花费的代价,使得每次从1开始,终点不限的情况 ...

最新文章

  1. 【jstl】jstl的基本操作
  2. Mybatisplus 自动生成字段 强制覆盖 null或者空字符串也覆盖
  3. mysql基础和高级整理_mysql基础整理01
  4. 【数据结构】树状数组
  5. GRUB系统引导器恢复
  6. python 控制雕刻机_GitHub - cdhigh/PrinterCnc: 废打印机改装的大行程“雕刻机”,可以制作PCB。...
  7. 水系图一般在哪里找得到_雨水排水系统施工及设备要点详解!
  8. 新电商正面迎战“阅读焦虑”
  9. 电子书CHM格式转换为PDF文件(转)
  10. MATLAB神经网络工具箱(参数注释)
  11. 关于消防装备管理系统于消防装备管理中应用的论述
  12. 【web】百度地图中心点始终出现在左上角,或者灰蒙蒙的
  13. 09Apache POI学习笔记
  14. HTML技能点--设置网页图标标志
  15. 郝萌主的微信公众号上线了
  16. 教你如何学模电——三极管篇
  17. bundle、chunk、module的区别
  18. MATLAB figure最大化
  19. 深度学习数据集(一)
  20. 论文阅读:PVO: Panoptic Visual Odometry

热门文章

  1. 关于JAVA项目中CLASSPATH路径详解
  2. 《How Tomcat Works》读书笔记(二)
  3. mac下导出kindle单词本的单词
  4. 20145106 《Java程序设计》第10周学习总结
  5. Markdown大法的尝试
  6. 查看Linux软件信息
  7. 如何解决多线程并发问题
  8. 【Laravel-海贼王系列】第七章,Pipeline 类解析
  9. 如何快速解决繁杂的国际化替换
  10. 在Android界面上显示和获取Logcat日志输出