[bzoj2668] [洛谷P3159] [cqoi2012] 交换棋子
Description###
有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态。要求第i行第j列的格子只能参与mi,j次交换。
Input###
第一行包含两个整数n,m(1<=n, m<=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
想法##
首先把题目说的不清楚的地方澄清一下:“第i行第j列的格子只能参与mi,j次交换”所说第i行第j列的棋子指的是每次交换后位于第i行第j列这个位置的棋子,可以是多个,而不是指最原始状态中第i行第j列那个特定的棋子。
很容易发现,我们可以只考虑白色棋子,只要它们都移动到目标状态,剩下的黑棋子也都到目标状态了。
不停地交换听起来好像比较棘手,但其实白棋子只有和身边的黑棋子交换位置才有用。
所以我们就是要给每个白棋子规划一条线路,让它们从原始位置变到目标位置。
很容易想到按原图建图,拆点~
但有个问题,若某个白格子经过某个格子,那么这个格子会被该白格子交换两次;而白格子原位置与目标位置只会被该白格子交换一次。
于是有一个更高级的拆点:一个点拆成三个!(id1,id2,id3)
id1到id2限制其他点与这个点交换的次数,id2到id3限制这个点与其他点交换的次数(即一个是进入的流量,一个是出去的流量)
建图,分类讨论。
- 对于原状态和目标状态均为黑的格子。
id1到id3连容量为cap/2,费用为1的边 - 对于原状态为白,目标状态为黑的格子。
S到id2连容量为1,费用为0的边
id1到id2连容量为cap/2,费用为1的边
id2到id3连容量为(cap+1)/2,费用为1的边 - 对于原状态为黑,目标状态为白的格子。
id2到T连容量为1,费用为0的边
id1到id2连容量为(cap+1)/2,费用为1的边
id2到id3连容量为cap/2,费用为1的边 - 对于原状态和目标状态均为白的格子。
S到id2连容量为1,费用为0的边
id2到T连容量为1,费用为0的边
id1到id2连容量为cap/2,费用为1的边
id2到id3连容量为cap/2,费用为1的边
之后向八连通的格子连边。
注意是某格子的id1连向八连通格子的id3,然后八连通的id1连向这个格子的id3
。。。还是有点复杂的。
接下来就跑个最小费用最大流,看流量是否为总白格子数。
若不是,则输出-1,否则答案为 最小费用/2
代码##
注意细节:
1.某些边的容量为(cap+1)/2,容易想错写成cap/2
2.数组要开够!!
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#define INF 1000000000
using namespace std;
const int N = 405;
const int M = 1205;
struct node{
int v,f,c;
node *next,*rev;
}pool[N*40],*h[M],*pree[M]; /**/
int cnt;
void addedge(int u,int v,int f,int c){
node *p=&pool[++cnt],*q=&pool[++cnt];
p->v=v;p->next=h[u];h[u]=p; p->f=f;p->c=c;p->rev=q;
q->v=u;q->next=h[v];h[v]=q; q->f=0;q->c=-c;q->rev=p;
}
int S,T;
int d[M],vis[M],pre[M];
queue<int> que;
bool spfa(){
int u,v;
while(!que.empty()) que.pop();
for(int i=S;i<=T;i++) d[i]=INF;
d[S]=0; vis[S]=1; que.push(S);
while(!que.empty()){
u=que.front(); que.pop();
vis[u]=0;
for(node *p=h[u];p;p=p->next)
if(p->f && d[v=p->v]>d[u]+p->c){
d[v]=d[u]+p->c;
pre[v]=u; pree[v]=p;
if(!vis[v]){
vis[v]=1;
que.push(v);
}
}
}
return d[T]!=INF;
}
void MCMF(int &f,int &c){
f=0; c=0;
int u,w;
while(spfa()){
u=T; w=INF;
while(u!=S){
w=min(w,pree[u]->f);
u=pre[u];
}
f+=w; c+=d[T]*w;
u=T;
while(u!=S){
pree[u]->f-=w;
pree[u]->rev->f+=w;
u=pre[u];
}
}
}
int n,m;
char a[23][23],b[23][23],ch[23][23];
int dre[8][2]={-1,-1,-1,0,-1,1,0,-1,0,1,1,-1,1,0,1,1};
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++) scanf("%s",a[i]);
for(int i=0;i<n;i++) scanf("%s",b[i]);
for(int i=0;i<n;i++) scanf("%s",ch[i]);
int w=n*m,id1,id2,id3,tot=0;
S=0; T=w*3+1;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++){
id1=i*m+j+1; id2=id1+w; id3=id2+w;
if(a[i][j]=='0' && b[i][j]=='0')
addedge(id1,id3,(ch[i][j]-'0')/2,2);
else if(a[i][j]=='1' && b[i][j]=='0'){
tot++;
addedge(S,id2,1,0);
addedge(id1,id2,(ch[i][j]-'0')/2,1);
addedge(id2,id3,(ch[i][j]-'0'+1)/2,1); /**/
}
else if(a[i][j]=='0' && b[i][j]=='1'){
addedge(id2,T,1,0);
addedge(id1,id2,(ch[i][j]-'0'+1)/2,1); /**/
addedge(id2,id3,(ch[i][j]-'0')/2,1);
}
else{
tot++;
addedge(S,id2,1,0); addedge(id2,T,1,0);
addedge(id1,id2,(ch[i][j]-'0')/2,1);
addedge(id2,id3,(ch[i][j]-'0')/2,1);
}
for(int k=0;k<8;k++){
int x=i+dre[k][0],y=j+dre[k][1];
if(x>=0 && x<n && y>=0 && y<m){
addedge(id3,x*m+y+1,INF,0);
addedge(x*m+y+1+2*w,id1,INF,0);
}
}
}
int f,c;
MCMF(f,c);
if(f<tot) printf("-1\n");
else printf("%d",c/2);
return 0;
}
[bzoj2668] [洛谷P3159] [cqoi2012] 交换棋子的更多相关文章
- 洛谷P3159 [CQOI2012]交换棋子
巧妙的拆点方式,首先把1看成黑点,0看成空的,几次交换就可以看成一条路径 1)从容量上看,这条路径为1-2-2-2-2-2----2-1 2)从费用上看,这条路径每条边费用都是1 于是用一种巧妙的拆点 ...
- P3159 [CQOI2012]交换棋子
思路 相当神奇的费用流拆点模型 最开始我想到把交换黑色棋子看成一个流流动的过程,流从一个节点流向另一个节点就是交换两个节点,然后把一个位置拆成两个点限制流量,然后就有了这样的建图方法 S向所有初始是黑 ...
- 【BZOJ2668】[cqoi2012]交换棋子 费用流
[BZOJ2668][cqoi2012]交换棋子 Description 有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态.要求第i行第j列 ...
- BZOJ2668: [cqoi2012]交换棋子
题解: 可以戳这里:http://www.cnblogs.com/zig-zag/archive/2013/04/21/3033485.html 其实自己yy一下就知道这样建图的正确性了. 感觉太神奇 ...
- BZOJ 2668: [cqoi2012]交换棋子
2668: [cqoi2012]交换棋子 Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 1112 Solved: 409[Submit][Status ...
- [cqoi2012]交换棋子
2668: [cqoi2012]交换棋子 Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 1334 Solved: 518[Submit][Stat ...
- [洛谷P3158] [CQOI2011]放棋子
洛谷题目链接:[CQOI2011]放棋子 题目描述 在一个m行n列的棋盘里放一些彩色的棋子,使得每个格子最多放一个棋子,且不同 颜色的棋子不能在同一行或者同一列.有多少祌方法?例如,n=m=3,有两个 ...
- 洛谷 P3182 [HAOI2016]放棋子(高精度,错排问题)
传送门 解题思路 不会错排问题的请移步——错排问题 && 洛谷 P1595 信封问题 这一道题其实就是求对于每一行的每一个棋子都放在没有障碍的地方的方案数. 因为障碍是每行.每列只有一 ...
- 洛谷P3158 [CQOI2011]放棋子 组合数学+DP
题意:在一个m行n列的棋盘里放一些彩色的棋子,使得每个格子最多放一个棋子,且不同颜色的棋子不能在同一行或者同一列.有多少祌方法? 解法:这道题不会做,太菜了qwq.题解是看洛谷大佬的. 设C是组合数, ...
随机推荐
- linux进程睡眠的介绍
对于一个进程"睡眠"意味着什么? 当一个进程被置为睡眠, 它被标识为处于一个特殊的状 态并且从调度器的运行队列中去除. 直到发生某些事情改变了那个状态, 这个进程将不被 在任何 C ...
- linux /proc 接口和共享中断
在系统中安装共享处理者不影响 /proc/stat, 它甚至不知道处理者. 但是, /proc/interrupts 稍稍变化. 所有同一个中断号的安装的处理者出现在 /proc/interrupts ...
- Jasypt加密SpringBoot配置文件
如果 SpringBoot 的 properties 文件中含有用户名密码等敏感信息,为了安全起见需要对明文密码加密.Jasypt 是用来加密的 jar 包. 1.引入 Jasypt 在 pom.xm ...
- SPOJ - DISUBSTR Distinct Substrings (后缀数组)
Given a string, we need to find the total number of its distinct substrings. Input T- number of test ...
- unmask
当我们登录系统之后创建一个文件总是有一个默认权限的,那么这个权限是怎么来的呢?这就是umask干的事情.umask 设置了用户创建文件的默认权限,它与chmod的效果刚好相反,umas ...
- springboot前后端分离项目redis做验证码及用户信息存储验证长时间不操作失效问题解决
1.错误回显:Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: MISCO ...
- 我的 2019 年 Python 文章榜单
现在是 2020 年的第一天,我相信从昨天开始,各位的信息流里肯定充斥了各式各样的年度盘点/回顾/总结/记录之类的内容.虽然来得稍晚了,但我还是想给诸位送上这一篇文章. 我将在本文中列出自己于 201 ...
- 全网最详细的Ceph14.2.5集群部署及配置文件详解,快来看看吧! -- <2>
部署Ceph集群 Ceph版本选择 Ceph版本来源介绍 Ceph 社区最新版本是 14,而 Ceph 12 是市面用的最广的稳定版本. 第一个 Ceph 版本是 0.1 ,要回溯到 2008 年 1 ...
- 1074 宇宙无敌加法器 (20分)C语言
地球人习惯使用十进制数,并且默认一个数字的每一位都是十进制的.而在 PAT 星人开挂的世界里,每个数字的每一位都是不同进制的,这种神奇的数字称为"PAT数".每个 PAT 星人都必 ...
- 极光推送SDK通过泰尔终端实验室检测,符合统一推送接口标准
1月7日,中国深圳--国内领先的开发者服务提供商极光(Aurora Mobile, NASDAQ:JG)宣布其旗下产品极光推送SDK通过中国信息通信研究院泰尔终端实验室的检测,其性能和接口标准符合统一 ...