思路

相当神奇的费用流拆点模型
最开始我想到把交换黑色棋子看成一个流流动的过程,流从一个节点流向另一个节点就是交换两个节点,然后把一个位置拆成两个点限制流量,然后就有了这样的建图方法
S向所有初始是黑色点的入点连cap=1,cost=0的边,最后是黑色点的出点向T连一条cap=1,cost=0的边,然后对应点的出点向它八连通的点的入点连一条cap=INF,cost=1的边,每个点的入点向出点连一条cap=limit,cost=0的边
看起来很靠谱,实际是假的
因为我们刚才的方法没有考虑到一条交换路径的两个端点只交换一次并且路径上其他点都交换了两次(也就是端点和路径上的其他点没有区别)
所以可以拆成三层图。
S向每个初始黑点的mid连边,每个最终黑点的mid向T连边,相邻点连边不变,
然后懒得讲了。。。。

代码

···cpp

include

include

include

include

include

using namespace std;
struct Edge{
int u,v,cap,cost,flow;
};
const int MAXN = 1550;
const int INF = 0x3f3f3f3f;
vector edges;
vector G[MAXN];
int d[MAXN],p[MAXN],a[MAXN],vis[MAXN],s,t,n,m;
queue q;
void addedge(int u,int v,int cap,int cost){
edges.push_back((Edge){u,v,cap,cost,0});
edges.push_back((Edge){v,u,0,-cost,0});
int cnt=edges.size();
G[u].push_back(cnt-2);
G[v].push_back(cnt-1);
}
bool spfa(int &cost,int &flow){
memset(d,0x3f,sizeof(d));
memset(p,0,sizeof(p));
q.push(s);
d[s]=0;
a[s]=INF;
p[s]=0;
vis[s]=true;
while(!q.empty()){
int x=q.front();
q.pop();
vis[x]=false;
for(int i=0;i<G[x].size();i++){
Edge &e = edges[G[x][i]];
if(e.cap>e.flow&&d[x]+e.cost<d[e.v]){
d[e.v]=d[x]+e.cost;
p[e.v]=G[x][i];
a[e.v]=min(a[x],e.cap-e.flow);
if(!vis[e.v]){
vis[e.v]=true;
q.push(e.v);
}
}
}
}
if(d[t]==INF)
return false;
flow+=a[t];
cost+=a[t]d[t];
for(int i=t;i!=s;i=edges[p[i]].u){
edges[p[i]].flow+=a[t];
edges[p[i]^1].flow-=a[t];
}
return true;
}
void MCMF(int &cost,int &flow){
cost=flow=0;
while(spfa(cost,flow));
}
inline int id(int x,int y){
return (x-1)
m+y;
}
char S[50];
int pre_map[30][30],bac_map[30][30];
int main(){
s=MAXN-2;
t=MAXN-3;
int cntb1=0,cntb2=0;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%s",S+1);
for(int j=1;j<=m;j++){
if(S[j]=='0'){
cntb1++;
addedge(s,id(i,j)+2nm,1,0);
}
pre_map[i][j]=S[j]-'0';
}
}
for(int i=1;i<=n;i++){
scanf("%s",S+1);
for(int j=1;j<=m;j++){
if(S[j]=='0'){
cntb2++;
addedge(id(i,j)+2nm,t,1,0);
}
bac_map[i][j]=S[j]-'0';
}
}
if(cntb1!=cntb2){
printf("%d\n",-1);
return 0;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
if(i!=1){//up
addedge(id(i,j)+nm,id(i-1,j),INF,1);
}
if(j!=1){//left
addedge(id(i,j)+n
m,id(i,j-1),INF,1);
}
if(i!=n){//down
addedge(id(i,j)+nm,id(i+1,j),INF,1);
}
if(j!=m){//right
addedge(id(i,j)+n
m,id(i,j+1),INF,1);
}
if(i!=1&&j!=1){//zuoshang
addedge(id(i,j)+nm,id(i-1,j-1),INF,1);
}
if(i!=n&&j!=1){//zuoxia
addedge(id(i,j)+n
m,id(i+1,j-1),INF,1);
}
if(i!=1&&j!=m){//youshang
addedge(id(i,j)+nm,id(i-1,j+1),INF,1);
}
if(i!=n&&j!=m){//youxia
addedge(id(i,j)+n
m,id(i+1,j+1),INF,1);
}
}
for(int i=1;i<=n;i++){
scanf("%s",S+1);
for(int j=1;j<=m;j++){
if(pre_map[i][j]==1&&bac_map[i][j]==0){
addedge(id(i,j),id(i,j)+2nm,(S[j]-'0'+1)/2,0);
addedge(id(i,j)+2nm,id(i,j)+nm,(S[j]-'0')/2,0);
}
if(pre_map[i][j]==0&&bac_map[i][j]==1){
addedge(id(i,j),id(i,j)+2
nm,(S[j]-'0')/2,0);
addedge(id(i,j)+2
nm,id(i,j)+nm,(S[j]-'0'+1)/2,0);
}
if(pre_map[i][j]==bac_map[i][j]){
addedge(id(i,j),id(i,j)+2nm,(S[j]-'0')/2,0);
addedge(id(i,j)+2nm,id(i,j)+n*m,(S[j]-'0')/2,0);
}
}
}
int cost=0,flow=0;
MCMF(cost,flow);
printf("%d\n",cost);
return 0;
}

···

P3159 [CQOI2012]交换棋子的更多相关文章

  1. [bzoj2668] [洛谷P3159] [cqoi2012] 交换棋子

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

  2. 洛谷P3159 [CQOI2012]交换棋子

    巧妙的拆点方式,首先把1看成黑点,0看成空的,几次交换就可以看成一条路径 1)从容量上看,这条路径为1-2-2-2-2-2----2-1 2)从费用上看,这条路径每条边费用都是1 于是用一种巧妙的拆点 ...

  3. BZOJ2668: [cqoi2012]交换棋子

    题解: 可以戳这里:http://www.cnblogs.com/zig-zag/archive/2013/04/21/3033485.html 其实自己yy一下就知道这样建图的正确性了. 感觉太神奇 ...

  4. BZOJ 2668: [cqoi2012]交换棋子

    2668: [cqoi2012]交换棋子 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 1112  Solved: 409[Submit][Status ...

  5. 【BZOJ2668】[cqoi2012]交换棋子 费用流

    [BZOJ2668][cqoi2012]交换棋子 Description 有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态.要求第i行第j列 ...

  6. [cqoi2012]交换棋子

      2668: [cqoi2012]交换棋子 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 1334  Solved: 518[Submit][Stat ...

  7. 洛谷 P3159(BZOJ 2668)[CQOI2012]交换棋子

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

  8. BZOJ2668:[CQOI2012]交换棋子——题解

    http://www.lydsy.com/JudgeOnline/problem.php?id=2668 https://www.luogu.org/problemnew/show/P3159#sub ...

  9. BZOJ.2668.[CQOI2012]交换棋子(费用流zkw)

    题目链接 首先黑白棋子的交换等价于黑棋子在白格子图上移动,都到达指定位置. 在这假设我们知道这题用网络流做. 那么黑棋到指定位置就是一条路径,考虑怎么用流模拟出这条路径. 我们发现除了路径的起点和终点 ...

随机推荐

  1. hdu5032 树状数组

    题意: 对于一个1000*1000的Mushroom, 起点在(1,1)给定一个斜率和一个x,求由斜率和x所对应的直线构成的三角形内蘑菇的总值. 每个点的对应的值为(x+A)(y+B) 解 每个点都有 ...

  2. 开源IOT平台

    用于IoT应用程序开发的10大开源软件: 1. DeviceHive DeviceHive基于AllJoyn的Data Art设备,同时也是AllSeen的联盟成员.这一款免费开源机器和机器通信(M2 ...

  3. 基于TCP/IP协议的socket通讯server

    思路: socket必须要随项目启动时启动,所以需用Spring自带的监听器,需要保持长连接,要用死循环,所以必须另外起线程,不能阻碍主线程运行 1.在项目的web.xml中配置listener &l ...

  4. Vue 的路由实现 Hash模式 和 History模式

    Hash 模式: Hash 模式的工作原理是onhashchange事件,Window对象可以监听这个事件... 可以通过改变路径的哈希值,来实现历史记录的保存,发生变化的hash 都会被浏览器给保存 ...

  5. Python pyodbc安装

    1)下面这个链接找个适合自己python版本的文件下载下来 https://pypi.org/project/pyodbc/#files 2)放到scripts下面 3) 在scripts路径上输入c ...

  6. django 常用方法总结 < 手写分页-上传头像-redis缓存,排行 ...>

    1.不使用自带模块<Paginator>的手写分页功能views.pydef post_list(request): page = request.GET.get('page', 1) # ...

  7. Linux基础命令---显示域名ypdomainname

    ypdomainname   ypdomainname指令显示由函数“getdomainname”返回的主机域名,使用这个指令也可以设置一个主机NIS/YP域名. 此命令的适用范围:RedHat.RH ...

  8. PIVOT(透视转换)和UNPIVOT(逆透视转换)

    一.原数据状态 二.手动写透视转换1 三.手动写透视转换2 四.PIVOT(透视转换)和UNPIVOT(逆透视转换)详细使用 使用标准SQL进行透视转换和逆视转换 --行列转换 create tabl ...

  9. StructureStreaming与kafka集成读取数据必要的jar包

    <dependency> <!--structurStreaming读取kafka1.0以下必须的jar--> <groupId>org.apache.spark& ...

  10. js获取浏览器类型和版本信息

    bro () { let broName = 'Runing' let strStart = 0 let strStop = 0 let temp = '' let userAgent = windo ...