传送门

好难的网络流啊,建图真的超难。

如果不告诉我是网络流的话,我估计就会写dfs了。

使用费用流解决本题,设点 $p[i][j]$ 的参与交换的次数上限为 $v[i][j]$ ,以下为建图方式:

  1. 将一个点分成三个点,分别为入点,原点和出点。

  2. 如果开始的图上该位置有棋子,那么从S到该点的原点连一条边权1,费用0的边

  3. 如果结束的图上该位置有棋子,那么从该点的原点到T连一条边权1,费用0的边

  4. 如果该点只在开始的图上出现,那么从该点的入点向原点连一条边权为 $v[i][j]/2$ ,费用为1的边,从该点的原点向出点连一条边权为 $(v[i][j]+1)/2$ ,费用为0的边

  5. 如果该点只在结束的图上出现,那么从该点的入点向原点连一条边权为 $(v[i][j]+1)/2$ ,费用为1的边,从该点的原点向出点连一条边权为 $v[i][j]/2$ ,费用为0的边

  6. 如果以上两点都不符合,那么从该点的入点向原点连一条边权为 $v[i][j]/2$ ,费用为1的边,从该点的原点向出点连一条边权为 $v[i][j]/2$ ,费用为0的边

——by zhouyonglong

我只是题解的搬运工。

最后把每个点的原点和它相邻的点的原点连一条容量为INF,费用为0的边

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 1000001
#define id(i, j, k) ((i - 1) * m + j + (k - 1) * n * m) using namespace std; int n, m, cnt, s, t, ans, sum1, sum2, sum;
int head[N], to[N], nex[N], val[N], cost[N], dis[N], pre[N], path[N];
char s1[21][21], s2[21][21], c[21][21];
const int dx[8] = {1, 1, 0, -1, -1, -1, 0, 1}, dy[8] = {0, 1, 1, 1, 0, -1, -1, -1};
bool vis[N]; inline int read()
{
int x = 0, f = 1;
char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
return x * f;
} inline void add(int x, int y, int z, int v)
{
to[cnt] = y;
val[cnt] = z;
cost[cnt] = v;
nex[cnt] = head[x];
head[x] = cnt++;
} inline bool spfa()
{
int i, u, v;
queue <int> q;
memset(vis, 0, sizeof(vis));
memset(pre, -1, sizeof(pre));
memset(dis, 127, sizeof(dis));
q.push(s);
dis[s] = 0;
while(!q.empty())
{
u = q.front();
vis[u] = 0;
q.pop();
for(i = head[u]; ~i; i = nex[i])
{
v = to[i];
if(val[i] && dis[v] > dis[u] + cost[i])
{
dis[v] = dis[u] + cost[i];
pre[v] = u;
path[v] = i;
if(!vis[v])
{
q.push(v);
vis[v] = 1;
}
}
}
}
return pre[t] != -1;
} inline void E()
{
puts("-1"), exit(0);
} int main()
{
int i, j, k, x, y, f;
n = read();
m = read();
s = 0, t = 3 * n * m + 1;
memset(head, -1, sizeof(head));
for(i = 1; i <= n; i++) scanf("%s", s1[i] + 1);
for(i = 1; i <= n; i++) scanf("%s", s2[i] + 1);
for(i = 1; i <= n; i++) scanf("%s", c[i] + 1);
for(i = 1; i <= n; i++)
for(j = 1; j <= m; j++)
{
c[i][j] -= '0';
if(s1[i][j] == '1')
{
sum1++;
add(s, id(i, j, 2), 1, 0);
add(id(i, j, 2), s, 0, 0);
}
if(s2[i][j] == '1')
{
sum2++;
add(id(i, j, 2), t, 1, 0);
add(t, id(i, j, 2), 0, 0);
}
if(s1[i][j] == '1' && s2[i][j] == '0')
{
add(id(i, j, 1), id(i, j, 2), c[i][j] / 2, 1);
add(id(i, j, 2), id(i, j, 1), 0, -1);
add(id(i, j, 2), id(i, j, 3), (c[i][j] + 1) / 2, 0);
add(id(i, j, 3), id(i, j, 2), 0, 0);
}
if(s1[i][j] == '0' && s2[i][j] == '1')
{
add(id(i, j, 1), id(i, j, 2), (c[i][j] + 1) / 2, 1);
add(id(i, j, 2), id(i, j, 1), 0, -1);
add(id(i, j, 2), id(i, j, 3), c[i][j] / 2, 0);
add(id(i, j, 3), id(i, j, 2), 0, 0);
}
if(s1[i][j] == s2[i][j])
{
add(id(i, j, 1), id(i, j, 2), c[i][j] / 2, 1);
add(id(i, j, 2), id(i, j, 1), 0, -1);
add(id(i, j, 2), id(i, j, 3), c[i][j] / 2, 0);
add(id(i, j, 3), id(i, j, 2), 0, 0);
}
for(k = 0; k < 8; k++)
{
x = i + dx[k];
y = j + dy[k];
if(1 <= x && x <= n && 1 <= y && y <= m)
{
add(id(i, j, 3), id(x, y, 1), 1e9, 0);
add(id(x, y, 1), id(i, j, 3), 0, 0);
}
}
}
if(sum1 != sum2) E();
while(spfa())
{
f = 1e9;
for(i = t; i != s; i = pre[i]) f = min(f, val[path[i]]);
sum += f;
ans += dis[t];
for(i = t; i != s; i = pre[i])
{
val[path[i]] -= f;
val[path[i] ^ 1] += f;
}
}
if(sum != sum1) E();
printf("%d\n", ans);
return 0;
}

  

[luoguP3159] [CQOI2012]交换棋子(最小费用最大流)的更多相关文章

  1. BZOJ 2668 [cqoi2012]交换棋子 | 最小费用最大流

    传送门 BZOJ 2668 题解 同时分别限制流入和流出次数,所以把一个点拆成三个:入点in(x).中间点mi(x).出点ou(x). 如果一个格子x在初始状态是黑点,则连(S, mi(x), 1, ...

  2. 【BZOJ-2668】交换棋子 最小费用最大流

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

  3. BZOJ2668:[CQOI2012]交换棋子(费用流)

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

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

    题目链接 BZOJ2668 题解 容易想到由\(S\)向初始的黑点连边,由终态的黑点向\(T\)连边,然后相邻的点间连边 但是这样满足不了交换次数的限制,也无法计算答案 考虑如何满足一个点的交换次数限 ...

  5. [CQOI2012] 交换棋子 (费用流)

    $pdf\space solution$    link #include<iostream> #include<cstring> #include<cstdio> ...

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

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

  7. [cqoi2012]交换棋子

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

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

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

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

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

随机推荐

  1. 【extjs6学习笔记】0.3 准备:系统架构

  2. 88E1111

    千兆网phy芯片 支持GMII,RGMII,MII等接口 具备4个GMII时钟模式 支持自适应功能 超低功耗模式 功率降低模式 MDC/MDIO/TWSI接口 支持10Mb/s,100Mb/s,100 ...

  3. setuid

    -r-s--x--x   #s就是setuid,仅可用在二进制文件,对目录设置无效

  4. Microsoft Sql server2005的安装步骤和常见问题解决方案

    一:安装sql server 2005过程中出现 如下问题:“选择的功能中没有任何功能可以安装或升级”: 解决方案:Microsoft SQL Server 2005→配置工具→SQL配置管理器→SQ ...

  5. 关键字: on

    关键字: on 数据库在通过连接两张或多张表来返回记录时,都会生成一张中间的临时表,然后再将这张临时表返回给用户. 在使用left jion时,on和where条件的区别如下: 1. on条件是在生成 ...

  6. CS193p Lecture 10 - Multithreating, UIScrollView

    Multithreating(多线程) 网络请求例子: NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithStrin ...

  7. 【译】Swift 字符串速查表

    [译]Swift 字符串速查表 2015-12-18 10:32 编辑: suiling 分类:Swift 来源:CocoaChina翻译活动 10 5585 Swift字符串 招聘信息: iOS高级 ...

  8. swift中使用sqlite3

    import Foundation /** 1. 打开数据库 2. 如果没有数据表,需要首先创表 3. 数据操作 */ class SQLite { var db: COpaquePointer = ...

  9. HTML5 FormData 模拟表单控件 支持异步上传二进制文件 移动端

    FormData是XMLHttpRequest Level 2添加的一个新的接口,利用FormData对象,我们可以通过JavaScript用一些键值对来模拟一系列表单控件,还可以使用XMLHttpR ...

  10. 简单css动画 fadeIn fadeOut flash

    考虑兼容性采用 -webkit- -o- -mos- -ms- @keyframes fadeIn{ 0%{ opacity: 0; display: block; } 100%{ opacity: ...