洛谷P3159 交换棋子 神奇的网络流
神奇的建模。。。原题链接
如果你真的把交换看成交换,就\(GG\)了。首先我们要把交换看成是白棋的移动。
然后,很容易的就想到建模的大致思路:建立超级源点S和超级汇点T,从S向初始局面每个白棋所在的格子连边,从目标局面每个白棋所在的格子向T连边,在相邻的格子中间加一些有限制的边,跑一波费用流。
那中间的那些边应该怎么加呢,先考虑把每个格子拆成两个点,中间连一条以交换次数为流量上限的边。但是这样会有问题,考虑某个白棋移动路径上的点,其实只有开头的格子交换了一次,中间的都是两次。只拆两个体现不了在中间或首尾的差别,那我们就拆三个。
把一个格子拆成三个点:TYPE1,TYPE2,TYPE3,分别表示流入,当前格子,流出。与S和T的连边改为向TYPE2连边,与相邻格子的连边在TYPE1与TYPE3之间连,另外从TYPE2向TYPE1和TYPE3连边,这样就能体现在路径上位置的差别了。
那这些边的容量和费用该怎么设置呢?
1.TYPE1与TYPE3之间的连边容量为\(INF\),费用为\(1\)。
2.S与TYPE2,TYPE2与T容量为\(1\),费用为\(0\)。
3.TYPE2与TYPE1和TYPE3的连边(重点):
对于某个格子(假设它的交换上限为\(w\),"/"号代表整除):
①若初始为白棋,目标为黑棋,则从TYPE1向TYPE2连一条容量为\(w/2\),费用为\(0\)的边,从TYPE2向TYPE3连一条容量为\((w+1)/2\),费用为\(0\)的边
②若初始为黑棋,目标为白棋,则从TYPE1向TYPE2连一条容量为\((w+1)/2\),费用为\(0\)的边,从TYPE2向TYPE3连一条容量为\(w/2\),费用为\(0\)的边
③若始末颜色相同,则从TYPE1向TYPE2连一条容量为\(w/2\),费用为\(0\)的边,从TYPE2向TYPE3连一条容量为\(w/2\),费用为\(0\)的边
为什么权值要这样设,因为我们要保持收支平衡,使得每个格子在交换之后的颜色是对的。还有我们考虑的是白棋的移动,因此①应该会多一次流出,于是我们尽量把零头分给TYPE2与TYPE3之间的那条边。②同理。
注意判\(-1\)!
#include <bits/stdc++.h>
using namespace std;
#define pii pair<int, int>
#define mp make_pair
#define pb push_back
#define N 10000
#define INF 0x3f3f3f3f
int n, m, S, T;
struct MCMF
{
struct Edge
{
int from, to, cap, flow, cost;
};
int S, T;
int d[N+5], a[N+5], vis[N+5], pre[N+5];
vector<int> G[N+5];
vector<Edge> edges;
void init(int S, int T)
{
this->S = S, this->T = T;
}
void addEdge(int from, int to, int cap, int cost)
{
edges.pb(Edge{from, to, cap, 0, cost}), edges.pb(Edge{to, from, 0, 0, -cost});
G[from].pb(edges.size()-2), G[to].pb(edges.size()-1);
}
int SPFA(int &flow, int &cost)
{
memset(d, 0x3f, sizeof d), memset(vis, 0, sizeof vis);
d[S] = 0, a[S] = INF, vis[S] = 1, pre[S] = 0;
queue<int> q;
q.push(S);
while(!q.empty())
{
int u = q.front(); q.pop();
vis[u] = 0;
for(int i = 0; i < G[u].size(); ++i)
{
Edge &e = edges[G[u][i]];
if(e.cap > e.flow && d[e.to] > d[u]+e.cost)
{
d[e.to] = d[u]+e.cost;
pre[e.to] = G[u][i];
a[e.to] = min(a[u], e.cap-e.flow);
if(!vis[e.to]) vis[e.to] = 1, q.push(e.to);
}
}
}
if(d[T] == INF) return 0;
int u = T;
flow += a[T], cost += d[T]*a[T];
while(u != S)
{
edges[pre[u]].flow += a[T], edges[pre[u]^1].flow -= a[T];
u = edges[pre[u]].from;
}
return 1;
}
pii minCost()
{
int flow = 0, cost = 0;
while(SPFA(flow, cost));
return mp(flow, cost);
}
}solver;
int Val(char c)
{
return c-'0';
}
int TYPE1(int i, int j)
{
return ((i-1)*m+j)*3-2;
}
int TYPE2(int i, int j)
{
return ((i-1)*m+j)*3-1;
}
int TYPE3(int i, int j)
{
return ((i-1)*m+j)*3;
}
int a[25][25], b[25][25], w[25][25];
int d[8][2] = {-1,0,1,0,0,-1,0,1,-1,-1,-1,1,1,-1,1,1};
//0:black 1:white
int main()
{
scanf("%d%d", &n, &m);
S = 0, T = 3*n*m+1;
solver.init(S, T);
char c;
int sum = 0;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j) cin >> c, a[i][j] = Val(c), sum += a[i][j];
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j) cin >> c, b[i][j] = Val(c);
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j) cin >> c, w[i][j] = Val(c);
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
{
if(a[i][j]) solver.addEdge(S, TYPE2(i, j), 1, 0);
if(b[i][j]) solver.addEdge(TYPE2(i, j), T, 1, 0);
if(a[i][j] == 0 && b[i][j] == 1)
solver.addEdge(TYPE1(i, j), TYPE2(i, j), (w[i][j]+1)/2, 0), solver.addEdge(TYPE2(i, j), TYPE3(i, j), w[i][j]/2, 0);
else if(a[i][j] == 1 && b[i][j] == 0)
solver.addEdge(TYPE1(i, j), TYPE2(i, j), w[i][j]/2, 0), solver.addEdge(TYPE2(i, j), TYPE3(i, j), (w[i][j]+1)/2, 0);
else
solver.addEdge(TYPE1(i, j), TYPE2(i, j), w[i][j]/2, 0), solver.addEdge(TYPE2(i, j), TYPE3(i, j), w[i][j]/2, 0);
for(int k = 0, ti, tj; k < 8; ++k)
{
ti = i+d[k][0], tj = j+d[k][1];
if(ti < 1 || ti > n || tj < 1 || tj > m) continue;
solver.addEdge(TYPE3(i, j), TYPE1(ti, tj), INF, 1);
}
}
pii res = solver.minCost();
if(res.first != sum) printf("-1\n");
else printf("%d\n", res.second);
return 0;
}
洛谷P3159 交换棋子 神奇的网络流的更多相关文章
- 【BZOJ1458】【洛谷4311】士兵占领(网络流)
[BZOJ1458][洛谷4311]士兵占领(网络流) 题面 BZOJ权限题,洛谷真好 Description 有一个M * N的棋盘,有的格子是障碍.现在你要选择一些格子来放置一些士兵,一个格子里最 ...
- 洛谷 P2763 试题库问题(网络流24题之一)
题目描述 «问题描述: 假设一个试题库中有n道试题.每道试题都标明了所属类别.同一道题可能有多个类别属性.现要从题库中抽取m 道题组成试卷.并要求试卷包含指定类型的试题.试设计一个满足要求的组卷算法. ...
- 洛谷 P3159(BZOJ 2668)[CQOI2012]交换棋子
有一个\(n\)行\(m\)列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态.要求第\(i\)行第\(j\)列的格子只能参与\(m[i][j]\)次交换 ...
- [bzoj2668] [洛谷P3159] [cqoi2012] 交换棋子
Description 有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态.要求第i行第j列的格子只能参与mi,j次交换. Input 第一行 ...
- 洛谷P3159 [CQOI2012]交换棋子
巧妙的拆点方式,首先把1看成黑点,0看成空的,几次交换就可以看成一条路径 1)从容量上看,这条路径为1-2-2-2-2-2----2-1 2)从费用上看,这条路径每条边费用都是1 于是用一种巧妙的拆点 ...
- 洛谷 [P2756] 飞行员配对方案问题 网络流实现
网络流实现二分图匹配 对于x集合的每一个点连一条从源点出发的容量为一的边,对于y集合的每一个点连一条到汇点的容量为一的边,跑最大流 #include <iostream> #include ...
- 洛谷P3158 放棋子 [CQOI2011] dp+数论
正解:dp+数论 解题报告: 传送门! 考虑对每种颜色的棋子单独考虑鸭,那显然有,当某一行或某一列已经被占据的时候,那一行/一列就不能再放别的颜色的棋子了,相当于直接把那一行/一列直接消了 显然就能考 ...
- 【Luogu】P3159交换棋子(超出我能力范围的费用流)
题目链接 明显超出我能力范围. 只放题解. 再放代码. #include<cstring> #include<algorithm> #include<cstdio> ...
- 洛谷P4009汽车加油行驶问题——网络流24题(最短路)
题目:https://www.luogu.org/problemnew/show/P4009 网络流24题中不是网络流的最短路题: 把每个点拆成各个油量上的点,根据要求连边即可: 注意:点数最大为10 ...
随机推荐
- Flask实战第5天:页面跳转和重定向
GET和POST请求 在网络请求中有许多请求方式,比如GET, POST, DELETE, PUT等,最常用的就是GET和POST GET 只会在服务器上获取资源,不会更改服务器的状态,这种请求方式推 ...
- APP请求服务器数据-HttpUrlConnection
1. 实例化URL对象 首先第一步实例化一个URL对象,传入参数为请求的数据的网址. URL url = new URL("http://www.imooc.com/api/teacher? ...
- 放下技术,是PM迈出的第一步
上一篇,我们从项目层面提出了PM的核心能力架构.今天,我想从公司层面,分析一下PM的核心能力架构中的过程能力,这也是PM当下最关心.最真切的痛点. 还记得上一篇我的同事老A吗? 为什么他能在知名外企带 ...
- SQL优化小技巧
我们要做到不但会写SQL,还要做到写出性能优良的SQL语句. 1.使用表的别名(Alias): 当在SQL语句中连接多个表时, 请使用表的别名并把别名前缀于每个Column上.这样一来,就可以减少解析 ...
- zabbix监控交换机状态
1.在Zabbix中添加主机 输入名称.群组和交换机IP(交换机要开启snmp) 2.创建监控项 输入OID和其它信息(键值随便填,但是不能和系统内的键值重复)OID获取方法可查看上一篇文章:http ...
- python3 haproxy配置文件操作练习
哈哈 老规矩 先来一个NLP第六条:咳咳! 六,重复旧的做法,只会得到旧的结果 做法有不同,结果才会有不同. 如果,你的事没有结果,改变你的做法.任何新的做法,都比旧的多一份成功的机会. 想明天比 ...
- RubyGems系列之RubyGems初识
转载请标明来源:https://www.cnblogs.com/zhanggui/p/9719291.html 一. 基础理解 RubyGems简称gems,它是一个用于对Ruby组件进行打包的Rub ...
- python接口自动化-session_自动发文
一.session简介 查看 requests.session() 帮助文档(只贴了一部分内容) import requests help(requests.session()) class Sess ...
- 如何通过Git将写好的项目发布到github上
1.在GitHub上创建新的项目文件 2.创建之后会进入新的页面,看到如下图的内容,将地址记下来 3.打开Git 4.进入项目本地所在目录 5.输入:git init 这个意思是在当前项目的目录中生成 ...
- [十二省联考2019]D1T1异或粽子
嘟嘟嘟 做这题之前,强烈推荐先把这道题切了P1631序列合并. 这两道题思路基本一模一样. 首先把异或处理成前缀异或,然后维护一个大根堆,每一次取出堆顶加到答案里面,然后把堆顶所在元素的次大的异或值放 ...