[CQOI2012][bzoj2668] 交换棋子 [费用流]
题面
思路
抖机灵
一开始看到这题我以为是棋盘模型-_-||
然而现实是骨感的
后来我尝试使用插头dp来交换,然后又惨死
最后我不得不把目光转向那个总能化腐朽为神奇的算法:网络流
思维
我们要先有一个思维的转变:要把棋盘上的“交换”操作,看成所有的黑色棋(白色棋等价)在移动
我们考虑令一个黑子往下移动一个
此时当前格子和下方格子的交换数都加一
考虑一条移动的路径,那么显然,这条路径两端的格子只进行了一次交换,但是路径上的所有格子进行了两次
我们可以考虑把这个过程变成网络流来做
但是有一个问题:一个格子如果本来就有一个黑棋,最后没有黑棋,或者本来是白棋,最后是黑棋,那么这个格子的收支会不平衡,也就是说我们硬做,连无向边的时候满足了流量平衡条件但却得不到最优解
而且如果每个格子只建一个点,也并不能把格子的交换次数限制考虑进去
那我们就要考虑拆点了
拆点
最基础的拆点:一个格子拆成两个,分别代表进入和走出,中间连一条容量为交换次数上限的边
但是这样有另一个问题:无法体现出路径两端的点和路径中间的点的区别(也就是如果“经过”了一个点,也只统计一点流量)
那我们再拆:把一个点拆成三个:left,now,right
从left向now连边、now向right连边,流量上限分别为限制的一半
这样就完美体现了只有流出、只有流入和流入流出都有的区别
相邻的点之间从right连向left
我们令源点向所有初始图中黑棋格子的now连边,汇点跟所有最终图中的黑棋格子的now连边,跑S-T最大流即可
问题
第一个大问题:如何解决上文中流量收支可能不平衡的问题?
答:如果该点是黑子->白子,那么这个点的出一定比入大一点流量;如果是白子->黑子,那么入一定比出大一点流量
第二个大问题:如何找最小?
做这个比较好办,把left-now和now-right边增加费用1就好了
结论&&最终实现方法
以下用<u,v,w,cap>表示u到v的有向边,费用w流量cap
建立费用流图,每个点拆成left,now,right
若该点在初始图中是黑的、最终图中是白的,那么连边(left,now,1,$\frac{limit}{2}$),(now,right,1,$\frac{limit+1}{2}$)
若该点在初始图中是白的、最终图中是黑的,那么连边(left,now,1,$\frac{limit+1}{2}$),(now,right,1,$\frac{limit}{2}$)
若该点在初始图和最终图中颜色相同,那么连边(left,now,1,$\frac{limit}{2}$),(now,right,1,$\frac{limit}{2}$)
其中limit表示这个格子的交换次数上限
建立附加源汇S-T
对于初始图中的黑点i,连边(S,now(i),0,1)
对于最终图中的黑点i,连边(now(i),T,0,1)
相邻的点i,j之间连边(right(i),left(j),0,inf)
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 1e9
#define tot (n*m*3)
#define left(i,j) ((i-1)*m+j)
#define now(i,j) (((i-1)*m+j)+n*m)
#define right(i,j) (((i-1)*m+j)+(n*m<<1))
using namespace std;
inline int read(){
int re=0,flag=1;char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') flag=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
return re*flag;
}
const int dx[9]={0,-1,-1,-1,0,0,1,1,1},dy[9]={0,-1,0,1,-1,1,-1,0,1};
int n,cnt=-1,m,first[2010],dis[2010],vis[2010],ans=0;
struct edge{
int to,next,w,cap;
}a[50010];
inline void add(int u,int v,int w,int cap){
a[++cnt]=(edge){v,first[u],w,cap};first[u]=cnt;
a[++cnt]=(edge){u,first[v],-w,0};first[v]=cnt;
}
int q[10010];
bool spfa(int s,int t){
int head=0,tail=1,i,v,u,w;
memset(dis,-1,sizeof(dis));memset(vis,0,sizeof(vis));
q[0]=t;vis[t]=1;dis[t]=0;
while(head<tail){
u=q[head++];vis[u]=0;
for(i=first[u];~i;i=a[i].next){
v=a[i].to;w=a[i].w;
if(a[i^1].cap&&((dis[v]==-1)||(dis[v]>dis[u]-w))){
dis[v]=dis[u]-w;
if(!vis[v]) q[tail++]=v,vis[v]=1;
}
}
}
return ~dis[s];
}
int dfs(int u,int t,int limit){
if(u==t||!limit){vis[u]=1;return limit;}
int i,v,f,flow=0,w;vis[u]=1;
for(i=first[u];~i;i=a[i].next){
v=a[i].to;w=a[i].w;
if(!vis[v]&&a[i].cap&&dis[v]==dis[u]-w){
if(!(f=dfs(v,t,min(limit,a[i].cap)))) continue;
a[i].cap-=f;a[i^1].cap+=f;
flow+=f;limit-=f;ans+=w*f;
if(!limit) return flow;
}
}
return flow;
}
int zkw(int s,int t){
int re=0;
while(spfa(s,t)){
vis[t]=1;
while(vis[t]){
memset(vis,0,sizeof(vis));
re+=dfs(s,t,inf);
}
}
return re;
}
int x1[30][30],x2[30][30];
int main(){
memset(first,-1,sizeof(first));
int i,j,t1=0,t2=0,ti,tj,k;char s[30];
n=read();m=read();
for(i=1;i<=n;i++){
scanf("%s",s);
for(j=1;j<=m;j++){
if(s[j-1]=='1'){
t1++;add(0,now(i,j),0,1);
x1[i][j]=1;
}
}
}
for(i=1;i<=n;i++){
scanf("%s",s);
for(j=1;j<=m;j++){
if(s[j-1]=='1'){
t2++;add(now(i,j),tot+1,0,1);
x2[i][j]=1;
}
}
}
if(t1!=t2){
puts("-1");return 0;
}
for(i=1;i<=n;i++){
scanf("%s",s);
for(j=1;j<=m;j++){
t2=s[j-1]-'0';
if(x1[i][j]==x2[i][j])
add(left(i,j),now(i,j),0,t2/2),add(now(i,j),right(i,j),0,t2/2);
if(x1[i][j]&&!x2[i][j])
add(left(i,j),now(i,j),0,t2/2),add(now(i,j),right(i,j),0,(t2+1)/2);
if(!x1[i][j]&&x2[i][j])
add(left(i,j),now(i,j),0,(t2+1)/2),add(now(i,j),right(i,j),0,t2/2);
}
}
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
for(k=1;k<=8;k++){
ti=i+dx[k];tj=j+dy[k];
if(ti<1||ti>n||tj<1||tj>m) continue;
add(right(i,j),left(ti,tj),1,inf);
}
}
}
if(zkw(0,tot+1)!=t1){
puts("-1");return 0;
}
cout<<ans<<endl;
}
[CQOI2012][bzoj2668] 交换棋子 [费用流]的更多相关文章
- 【BZOJ2668】[cqoi2012]交换棋子 费用流
[BZOJ2668][cqoi2012]交换棋子 Description 有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态.要求第i行第j列 ...
- BZOJ2668: [cqoi2012]交换棋子(费用流)
Description 有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态.要求第i行第j列的格子只能参与mi,j次交换. Input 第一行 ...
- [CQOI2012] 交换棋子 - 费用流
有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态.要求第i行第j列的格子只能参与mi,j次交换. Solution 一个点拆三份,入点,主点 ...
- BZOJ.2668.[CQOI2012]交换棋子(费用流zkw)
题目链接 首先黑白棋子的交换等价于黑棋子在白格子图上移动,都到达指定位置. 在这假设我们知道这题用网络流做. 那么黑棋到指定位置就是一条路径,考虑怎么用流模拟出这条路径. 我们发现除了路径的起点和终点 ...
- 【BZOJ】【2668】【CQOI2012】交换棋子
网络流/费用流 跪跪跪,居然还可以这样建图…… 题解:http://www.cnblogs.com/zig-zag/archive/2013/04/21/3033485.html 考虑每个点的交换限制 ...
- [bzoj2668]交换棋子
基本思路是,要让所有黑点都相对应(所以首先判断黑点的个数).如果没有交换限制,可以按以下方法建图:源点向所有初始黑点连(1,0)的边,最终黑点向汇点连(1,0)的边,相邻的两点连边(inf,1),最小 ...
- BZOJ2668:[CQOI2012]交换棋子(费用流)
题目描述 有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态.要求第i行第j列的格子只能参与mi,j次交换. 输入输出格式 输入格式: 第一行 ...
- 【BZOJ-2668】交换棋子 最小费用最大流
2668: [cqoi2012]交换棋子 Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 1055 Solved: 388[Submit][Status ...
- [luoguP3159] [CQOI2012]交换棋子(最小费用最大流)
传送门 好难的网络流啊,建图真的超难. 如果不告诉我是网络流的话,我估计就会写dfs了. 使用费用流解决本题,设点 $p[i][j]$ 的参与交换的次数上限为 $v[i][j]$ ,以下为建图方式: ...
随机推荐
- POJ 3624 Charm Bracelet(01背包模板)
Charm Bracelet Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 45191 Accepted: 19318 ...
- virtualbox下安装的纯净版centOS7,无法访问外网
virtualbox下安装的纯净版centOS7,网络设置如下: 需要在/etc/sysconfig/network-scripts/下编辑ifcfg-enp0s3,其中,NOBOOT设置成也是,就可 ...
- java selenium webdriver第二讲 页面元素定位
自动化测试实施过程中,测试程序中常用的页面操作有三个步骤 1.定位网页上的页面元素,并存储到一个变量中 2.对变量中存储的页面元素进行操作,单击,下拉或者输入文字等 3.设定页面元素的操作值,比如,选 ...
- SRW锁的使用
SRWLock的目的和关键段相同:对一个资源进行保护,不让其它线程访问它.但是,与关键段不同的是,SRWLock允许我们区分哪些想要读取资源的值 的线程(读取者线程)和想要更新资源的值的线程(写入者线 ...
- 跨数据文件删除flashback database
Oracle flashback database的使用有一些限制,其中最主要的是flashback database不支持跨数据文件删除闪回和不支持跨数据文件shrink闪回.对于已经删除的数据文件 ...
- 问题:C#控制台 停留;结果:c#控制台如何延时显示
Thread.Sleep(毫秒数);//比如Thread.Sleep(2000)即为延时2秒需using System.Threading; 随笔5 - C#控制台窗口的显示与隐藏 1. 定义一个Co ...
- MySQL学习笔记之一---字符编码和字符集
前言: 一般来说,出现中文乱码,都是客户端和服务端字符集不匹配导致的原因. (默认未指定字符集创建的数据库表,都是latinl字符集, 强烈建议使用utf8字符集) 保证不出现乱码的思想:保证客户 ...
- xcode编写c/c++静态库使用系统头文件问题
c/c++编写的静态库中有引用ios系统头文件比如: #include <EGL/egl.h> 在xcode编译的时候需要设置静态库程序: Build Settings-Header Se ...
- Windows10更新后无限重启
以安全模式进入系统,禁用或卸载显卡驱动. 重启后重新安装驱动.
- 自定义Android Studio方法注释模板
前言 你们从Eclipse转到Android Studio的时候,是不是会怀念Eclipse的方法注释模版? 敲/**加回车,模板就出来了,而Android Studio却不能自定义(或者我没有找到) ...