【BZOJ-2668】交换棋子 最小费用最大流
2668: [cqoi2012]交换棋子
Time Limit: 3 Sec Memory Limit: 128 MB
Submit: 1055 Solved: 388
[Submit][Status][Discuss]
Description
Input
Output
Sample Input
110
000
001
000
110
100
222
222
222
Sample Output
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】交换棋子 最小费用最大流相关推荐
- BZOJ 1070: [SCOI2007]修车(最小费用最大流)
建图很神奇..建完图其实就是裸的费用流了.. -------------------------------------------------------------- #include<cs ...
- BZOJ 2597 剪刀石头布(最小费用最大流)(WC2007)
Description 在一些一对一游戏的比赛(如下棋.乒乓球和羽毛球的单打)中,我们经常会遇到A胜过B,B胜过C而C又胜过A的有趣情况,不妨形象的称之为剪刀石头布情况.有的时候,无聊的人们会津津乐道 ...
- bzoj 1834: [ZJOI2010]network 网络扩容【最大流+最小费用最大流】
第一问直接跑最大流即可.建图的时候按照费用流建,费用为0. 对于第二问,在第一问dinic剩下的残量网络上建图,对原图的每条边(i,j),建(i,j,inf,cij),表示可以用c的花费增广这条路.然 ...
- BZOJ 1221: [HNOI2001] 软件开发(最小费用最大流)
不知道为什么这么慢.... 费用流,拆点.... --------------------------------------------------------------------------- ...
- bzoj 2245 [SDOI2011]工作安排【最小费用最大流】
其实不用拆点,对于每个人我们假装他是\( s[i]+1 \)个点,可以由他向T点分别连\( s[i]+1 \)条边,容量为\( t[i][j]-t[i][j-1]\),由S点向所有产品i连容量为c[i ...
- bzoj 1070: [SCOI2007]修车【最小费用最大流】
一开始从客人角度想的,怎么建都不对 从一个修车工所接待的所有顾客花费的总时间来看,设一共有x个人,那么第一个修的对总时间的贡献是x*w1,第二个是(x-1)*w2-以此类推.所以把第i个修车工拆成n组 ...
- 有下界的最小费用可行流2.0(bzoj 3876: [Ahoi2014]支线剧情)
什么是有下界的最小费用可行流? 平时来讲都是最小费用最大流,也就是在满流的前提条件下费用尽可能的少,而最小费用可行流一般不要求满流,但是每条边都有最小流量要求(比如经过边e(u,v)的流量不能少于4等 ...
- BZOJ 1061 志愿者招募(最小费用最大流)
题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1061 题意:申奥成功后,布布经过不懈努力,终于 成为奥组委下属公司人力资源部门的主管.布 ...
- 有上下限的最小费用可行流
例题 BZOJ 3876 题意,有N个点,并且有一些边,每个点必须要经过一次,但是不限经过次数,也就是有下界的情况,且下界为1,现在每次的起点都是1,问至少花费的代价,使得每次从1开始,终点不限的情况 ...
最新文章
- 【jstl】jstl的基本操作
- Mybatisplus 自动生成字段 强制覆盖 null或者空字符串也覆盖
- mysql基础和高级整理_mysql基础整理01
- 【数据结构】树状数组
- GRUB系统引导器恢复
- python 控制雕刻机_GitHub - cdhigh/PrinterCnc: 废打印机改装的大行程“雕刻机”,可以制作PCB。...
- 水系图一般在哪里找得到_雨水排水系统施工及设备要点详解!
- 新电商正面迎战“阅读焦虑”
- 电子书CHM格式转换为PDF文件(转)
- MATLAB神经网络工具箱(参数注释)
- 关于消防装备管理系统于消防装备管理中应用的论述
- 【web】百度地图中心点始终出现在左上角,或者灰蒙蒙的
- 09Apache POI学习笔记
- HTML技能点--设置网页图标标志
- 郝萌主的微信公众号上线了
- 教你如何学模电——三极管篇
- bundle、chunk、module的区别
- MATLAB figure最大化
- 深度学习数据集(一)
- 论文阅读:PVO: Panoptic Visual Odometry