传送门

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

如果不告诉我是网络流的话,我估计就会写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. iOS:让UIView覆盖导航栏

    当我们想做一个弹出式菜单时,想将导航栏也一起盖住不显示的话,可以用如下语句实现: UIView* myView = /* 你自定义的view */; UIWindow* currentWindow = ...

  2. moment算本月开始日期和结束日期

    moment算本月开始日期和结束日期 1.引入moment.js var vStartDate=new moment().add('month',addMonth).format("YYYY ...

  3. MySQL存储过程简介和引擎说明

  4. leetcode 4.两个排序数组的中位数

    题目: 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2 . 请找出这两个有序数组的中位数.要求算法的时间复杂度为 O(log (m+n)) . 你可以假设 nums1 和 nums ...

  5. fun下载内容批量收集

    1.download title and url #!/usr/bin/env python #-*- coding:utf-8 -*- import re, urllib2,threading de ...

  6. Luogu P2397 yyy loves Maths VI (mode)

    题目传送门 虽然只是一道黄题,但还是学到了一点新知识-- 摩尔投票法 用\(O(1)\)的内存,\(O(n)\)的时间来找出一串长度为n的数中的众数,前提是众数出现的次数要大于\(n/2\) 方法很简 ...

  7. XAMPP虚拟主机配置--20150423

    你需要一些顶级域名访问方式来访问你本地的项目文件而不是目录方式访问,这时候就需要配置虚拟主机,给你的目录绑定一个域名(本地的话可以通过修改 hosts 文件随便绑定什么域名比如 www.a.com 或 ...

  8. Wash!!(HDU_6000)

    传送门:Wash! 题意:有n台洗衣机,m台烘干机,给出了每台机器处理意见衣服的时间,而且没见机器同时只能处理一件衣服.问如何选择机器才能使洗完衣服的时间最短. 思路:建两个优先队列,一个表示洗衣机, ...

  9. windows使用文件服务器搭建Git服务器

    背景: 1.windows下搭建git服务器. 2.git服务器搭建在局域网文件共享区中. 3.没有复杂的权限控制,文件共享区都有访问权限. 步骤: 1.文件共享区中创建git远程仓库. 2.本地克隆 ...

  10. 【Java_多线程并发编程】JUC原子类——4种原子类

    根据修改的数据类型,可以将JUC包中的原子操作类可以分为4种,分别是: 1. 基本类型: AtomicInteger, AtomicLong, AtomicBoolean ;2. 数组类型: Atom ...