[二分图&最小割]
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是一个思想有些保守的高中老师.有一次,他需要带一些学生出去旅行,但又怕其中一些学生在旅行中萌生爱意.为了降低这种事情发生的概率,他决定确保带出去的任意两个学生至少要满足下面四条中的一条 ...
随机推荐
- 【Network】一张图看懂 Reactor 与 Proactor 模型的区别
首先来看看Reactor模式,Reactor模式应用于同步I/O的场景.我们以读操作为例来看看Reactor中的具体步骤:读取操作:1. 应用程序注册读就需事件和相关联的事件处理器2. 事件分离器等待 ...
- 【OpenStack】OpenStack系列1之Python虚拟环境搭建
安装virtualenv相关软件包 安装:yum install python-virtualenv* -y 简介,安装包主要包括, python-virtualenv:virtualenv用于创建独 ...
- 关于Linux下进程间使用共享内存和信号量通信的时的编译问题
今天在编译一个使用信号量实现进程同步时,出现了库函数不存在的问题.如下图 编译结果实际上是说,没include相应的头文件,或是头文件不存在(即系统不支持该库函数) 但我man shm_open是可以 ...
- django LDAP
> http://goodosoft.github.io/2015/02/25/Using-AD-as-authentication-for-Django/ > http://my.osc ...
- jdk新特性
自动拆装箱子: import org.junit.Test; public class Demo { /* * 自动拆装箱 * */ @Test public void ZhuangXiang() { ...
- iOS 中contraints居中对齐的一点心得
今天遇到一个布局问题,先看图 如何让第二个控件在第一个和第三个的竖直上的中间呢?contraint不能直接实现这样的效果.我们可以再添加一个控件,如图 这样之后,再把第二个控件和这个新添加的控件设置为 ...
- JavaEE填空与判断
Java EE软件工程师认证考试 试题库- 填空题和选择题 一. 填空题 1. HTML网页文件的标记是__html__,网页文件的主体标记是_body__,标记页面标题的标记是__tit ...
- MapReduce:详解Shuffle过程(转)
/** * author : 冶秀刚 * mail : dennyy99@gmail.com */ Shuffle过程是MapReduce的核心,也被称为奇迹发生的地方.要想理解MapRedu ...
- 忘记oracle的sys用户密码怎么修改
转分类: Linux 一.忘记除SYS.SYSTEM用户之外的用户的登录密码. 用SYS (或SYSTEM)用户登录: CONN SYS/PASS_WORD AS SYSDBA; 使用如下语句修改 ...
- 调试工具GDB详解
1 简介 2 生成调试信息 3 启动GDB 的方法 4 程序运行上下文 4.1 程序运行参数 4.2 工作目录 4.3 程序的输入输出 5 设置断点 5.1 简单断点 5.2 多文件设置断点 5.3 ...