[二分图&最小割]
OTL@assassain
反转源汇的模型: 给定一个二分图,同时选择集合中的两个点会有一个代价,选择每一个点有一个收益,问最大收益是多少
(即两个点在不同的集合中是有冲突关系的)
解法: 用最小割模型解决,通过反转源汇来表示冲突关系,用源S汇T表示选或不选,左边的黑点向S连黑点选择的收益(如果这条边割掉了就代表没有选择这个黑点,要减掉这个代价),向T连黑点不选择的收益(可以没有)。右边的白点向S连白点不选择的收益,向T连白点选择的收益(此时把S,T和上述反转了一下)。那么原图中两个点共同选择的代价就是在网络流图中直接连原来的权值即可
其实模型和另外一个模型很有对比意义,即最大权闭合子图
模型是给定一个二分图,选择其中一个点会有另一个点必须被选择(这时可以用inf的边来表示冲突关系),选择的收益有正有负(也可以看做有加有减),问最大收益。
那么此时两个点在相同的集合中是没有矛盾的,在不同的集合中是有代价的,此时不需要反转源汇,直接建图跑最小割就可以了
最大利益 = 所有点正权值之和 - 最小割
/*--------------------------------华丽丽的分割线--------------------------------*/
[国家集训队2011]圈地计划 网格图黑白染色,注意连双向边
#define MAXN 110
#include <bits/stdc++.h> using namespace std;
const int inf = 0x7fffffff / 2;
const int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1}; int n, m, S, T, a[MAXN][MAXN], b[MAXN][MAXN], c[MAXN][MAXN];
int h[MAXN*MAXN], cnt = 1;
struct Edge { int to, nxt, w; } edge[2000010];
void addedge(int u, int v, int w) {
edge[++ cnt] = (Edge){v, h[u], w}; h[u] = cnt;
edge[++ cnt] = (Edge){u, h[v], 0}; h[v] = cnt;
} int que[MAXN*MAXN], d[MAXN*MAXN];
bool BFS() {
int head = 0, tail = 0;
for(int i = S ; i <= T ; ++ i) d[i] = -1;
que[tail ++] = S, d[S] = 0;
while(head != tail) {
int u = que[head ++];
for(int i = h[u] ; i ; i = edge[i].nxt) {
if(edge[i].w == 0) continue;
int v = edge[i].to;
if(d[v] == -1) d[v] = d[u] + 1, que[tail ++] = v;
}
} return d[T] != -1;
} int DFS(int x, int a) {
if(x == T || a == 0) return a;
int used = 0, f;
for(int i = h[x] ; i ; i = edge[i].nxt) {
int v = edge[i].to;
if(d[v] == d[x] + 1) {
f = DFS(v, min(a-used, edge[i].w));
edge[i].w -= f;
edge[i^1].w += f;
used += f;
if(used == a) return used;
}
}
if(!used)d[x] = -1;
return used;
} int Dinic() {
int ret = 0;
while(BFS())
ret += DFS(S, inf);
return ret;
} int main() {
freopen("nt2011_land.in", "r", stdin);
freopen("nt2011_land.out", "w", stdout);
scanf("%d%d", &n, &m);
for(int i = 1 ; i <= n ; ++ i)
for(int j = 1 ; j <= m ; ++ j)
scanf("%d", &a[i][j]);
for(int i = 1 ; i <= n ; ++ i)
for(int j = 1 ; j <= m ; ++ j)
scanf("%d", &b[i][j]);
for(int i = 1 ; i <= n ; ++ i)
for(int j = 1 ; j <= m ; ++ j)
scanf("%d", &c[i][j]);
static int id[MAXN][MAXN], idfclock = 0;
for(int i = 1 ; i <= n ; ++ i)
for(int j = 1 ; j <= m ; ++ j)
id[i][j] = ++ idfclock;
S = 0, T = ++ idfclock;
for(int i = 1 ; i <= n ; ++ i)
for(int j = 1 ; j <= m ; ++ j) {
if(i + j & 1) {
addedge(S, id[i][j], a[i][j]);
addedge(id[i][j], T, b[i][j]);
}
else {
addedge(S, id[i][j], b[i][j]);
addedge(id[i][j], T, a[i][j]);
}
for(int k = 0 ; k < 4 ; ++ k) {
int x = i + dx[k], y = j + dy[k];
if(id[x][y])addedge(id[i][j], id[x][y], c[i][j] + c[x][y]);
}
}
int ans = 0;
for(int i = 1 ; i <= n ; ++ i)
for(int j = 1 ; j <= m ; ++ j) {
int cn = 0;
for(int k = 0 ; k < 4 ; ++ k) {
int x = i + dx[k], y = j + dy[k];
if(id[x][y]) cn ++;
}
ans += a[i][j] + b[i][j] + c[i][j] * cn;
} printf("%d\n", ans - Dinic());
return 0;
}
[国家集训队2011]男生女生
求一个最大的二分图的子图,使得左边的每一个点向右边的每一个点都有边相连(第一问)
转化成最小割模型后,表示的冲突关系是左边选择一个点,右边和它没有边相连的点就不能选择。
选择一个点的收益为1,不选择的收益为0
那么这道题的建图就是从S->左边的点,右边的点->T连边,中间因为永远不能割断所以边权为inf
还有一些特殊的地方需要处理,但是和反转源汇建图没有太大关系,因此不再赘述
#define MAXN 105
#include <bits/stdc++.h> using namespace std;
const int inf = 0x7fffffff, md = 19921228; int n, m, k;
bool a[MAXN][MAXN]; int h[MAXN], cnt = 1, S, T;
struct Edge { int to, nxt, w; } edge[1000010];
void addedge(int u, int v, int w) {
edge[++ cnt] = (Edge){v, h[u], w}; h[u] = cnt;
edge[++ cnt] = (Edge){u, h[v], 0}; h[v] = cnt;
} int d[MAXN], que[MAXN]; bool BFS() {
int head = 0, tail = 0;
memset(d, -1, sizeof d);
que[tail ++] = S, d[S] = 0;
while(head != tail) {
int u = que[head ++];
for(int i = h[u] ; i ; i = edge[i].nxt) {
if(!edge[i].w) continue;
int v = edge[i].to;
if(d[v] == -1) que[tail ++] = v, d[v] = d[u] + 1;
}
} return d[T] != -1;
} int DFS(int x, int a) {
if(x == T || a == 0) return a;
int used = 0, f;
for(int i = h[x] ; i ; i = edge[i].nxt) {
int v = edge[i].to;
if(d[v] == d[x] + 1) {
f = DFS(v, min(a-used, edge[i].w));
edge[i].w -= f;
edge[i^1].w += f;
used += f;
if(used == a) return used;
}
}
if(!used)d[x] = -1;
return used;
} int Dinic() {
int ret = 0;
while(BFS())
ret += DFS(S, inf);
return ret;
} int main() {
freopen("boygirl.in", "r", stdin);
freopen("boygirl.out", "w", stdout);
scanf("%d%d%d", &n, &k, &m);
int u, v;
for(int i = 1 ; i <= m ; ++ i) {
scanf("%d%d", &u, &v);
a[u][v] = true;
} T = n+n+1;
for(int i = 1 ; i <= n ; ++ i) {
addedge(S, i, 100), addedge(i+n, T, 99);
for(int j = 1 ; j <= n ; ++ j)
if(!a[i][j])addedge(i, j+n, inf);
} int fw = Dinic();
int a = fw - fw/99*99, b = fw/99 - a;
a = n-a, b = n-b; static int C[2510][2510];
static long long f[MAXN][MAXN];
memset(C, 0, sizeof C);
memset(f, 0, sizeof f); for(int i = 0 ; i <= a*b ; ++ i) {
C[i][0] = 1;
for(int j = 1 ; j <= i ; ++ j)
C[i][j] = (C[i-1][j] + C[i-1][j-1]) % md;
} for(int i = 1 ; i <= a ; ++ i) {
for(int j = 1 ; j <= b ; ++ j) {
f[i][j] = C[i*j][k];
for(int k = 1 ; k <= i ; ++ k)
for(int l = 1 ; l <= j ; ++ l)
if(i != k || l != j)
f[i][j] = (f[i][j] - f[k][l]*C[i][k]%md*C[j][l]%md + md) % md;
}
} printf("%d %d\n%lld\n", a, b, f[a][b]);
return 0;
}
[二分图&最小割]的更多相关文章
- BZOJ 3275: Number (二分图最小割)
题意 有nnn个数,其中同时满足下面两个条件的数对不能同时选,求选出一些数让和最大. 若两个数aaa,bbb同时满足以下条件,则aaa,bbb不能同时被选 存在正整数ccc,使a∗a+b∗b=c∗ca ...
- bzoj 3158 千钧一发(最小割)
3158: 千钧一发 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 767 Solved: 290[Submit][Status][Discuss] ...
- [TJOI2013]攻击装置(网络流,最小割)
前言 网络流被hbx吊起来打 Solution 考虑一下这个走法是不是和象棋中马的走法一模一样(废话) 那么显然我每一次移动是走三次,如果将棋盘二分图染色一下,不就是每一次只能走到另一个颜色的吗? 然 ...
- 1934. [SHOI2007]善意的投票【最小割】
Description 幼儿园里有n个小朋友打算通过投票来决定睡不睡午觉.对他们来说,这个问题并不是很重要,于是他们决定发扬谦让精神.虽然每个人都有自己的主见,但是为了照顾一下自己朋友的想法,他们也可 ...
- 二分图&网络流&最小割等问题的总结
二分图基础: 最大匹配:匈牙利算法 最小点覆盖=最大匹配 最小边覆盖=总节点数-最大匹配 最大独立集=点数-最大匹配 网络流: 技巧: 1.拆点为边,即一个点有限制,可将其转化为边 BZOJ1066, ...
- POJ 2125 Destroying The Graph (二分图最小点权覆盖集+输出最小割方案)
题意 有一个图, 两种操作,一种是删除某点的所有出边,一种是删除某点的所有入边,各个点的不同操作分别有一个花费,现在我们想把这个图的边都删除掉,需要的最小花费是多少. 思路 很明显的二分图最小点权覆盖 ...
- POJ2125 Destroying The Graph 二分图 + 最小点权覆盖 + 最小割
思路来源:http://blog.csdn.net/lenleaves/article/details/7873441 求最小点权覆盖,同样求一个最小割,但是要求出割去了那些边, 只要用最终的剩余网络 ...
- 【最小割/二分图最大独立集】【网络流24题】【P2774】 方格取数问题
Description 给定一个 \(n~\times~m\) 的矩阵,每个位置有一个正整数,选择一些互不相邻的数,最大化权值和 Limitation \(1~\leq~n,~m~\leq~100\) ...
- 【LA3415 训练指南】保守的老师 【二分图最大独立集,最小割】
题意 Frank是一个思想有些保守的高中老师.有一次,他需要带一些学生出去旅行,但又怕其中一些学生在旅行中萌生爱意.为了降低这种事情发生的概率,他决定确保带出去的任意两个学生至少要满足下面四条中的一条 ...
随机推荐
- MYSQL随机抽取查询 MySQL Order By Rand()效率问题
MYSQL随机抽取查询:MySQL Order By Rand()效率问题一直是开发人员的常见问题,俺们不是DBA,没有那么牛B,所只能慢慢研究咯,最近由于项目问题,需要大概研究了一下MYSQL的随机 ...
- Counting Bits
Given a non negative integer number num. For every numbers i in the range 0 ≤ i ≤ num calculate the ...
- Selenium webdriver 学习总结-元素定位
Selenium webdriver 学习总结-元素定位 webdriver提供了丰富的API,有多种定位策略:id,name,css选择器,xpath等,其中css选择器定位元素效率相比xpath要 ...
- 在64位的linux上运行32位的程序
1.症状 (1)执行bin文件时提示:No such file or directory (2)ldd bin文件 的输出为: not a dynamic executable (3)file bi ...
- WordPress前台后台页面打开慢的解决方法
写个人网站用WordPress程序是一个不错的选择,但是目前安装之后速度很慢,后台配置页面半天打不开,在网上查了一下原来是Google被墙导致,WordPress默认模板会加载谷歌的open-sans ...
- 11.python之线程,协程,进程,
一,进程与线程 1.什么是线程 线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行 ...
- Java Hour 25 Packages
有句名言,叫做10000小时成为某一个领域的专家.姑且不辩论这句话是否正确,让我们到达10000小时的时候再回头来看吧. 25 Hours. Packages Programs are organiz ...
- C# 从入门到精通
int i=null; int? i=null; 我们用一个问号 来指定这个值类型是可空的 属性是只读的
- 开源的DevOps开发工具箱
DevOps是一组过程.方法与系统的统称,用于促进开发(应用程序/软件工程).技术运营和质量保障(QA)部门之间的沟通.协作与整合.在DevOps的整个流程中,使用一些开源工具可以促进开发与运维之间的 ...
- CC2540开发板学习笔记(四)——定时器
一.实验内容 分别使用定时器T1和T3使得LED周期性闪烁 二.实验过程 1.定时器T1(查询IRCON来控制) (1)需要调配的寄存器 T1CTL(0XE4) Timer1控制寄存器 BIT3, ...