LOJ6001 「网络流 24 题
来源:互联网 发布:编程语言c 怎么用 编辑:程序博客网 时间:2024/06/06 21:00
大家都很强, 可与之共勉 。
题意:
现在您有
题解:
这道题是求一个坠大权闭合子图(话说这只图本来就是闭合的)。啊啊啊她有负边怎么办怎么办,好难过
首先呢,闭合图就是原图的一个子图,如果一个点
先来分析一下:
这是一个二分图,每个实验向需要的仪器连有向边,实验的点权为正,仪器的点权为负,要求最大权闭合图。
这是一个选或不选的问题,所以可以转化成最小割的模型,把选的归为
S 集,不选的归为T 集。但是要求获利最大,最小割是最小,所以我们要换个角度,要求扣除的钱最少,因为所有实验的前都加起来是一定的。在最小割中,如果把S到所有试验表示的点连一条容量为奖励的钱(
A 类弧),所有仪器到T 连一条容量为启动仪器的钱(B 类弧),如果把A类弧割掉了,那么对应的那个实验就归到了T 集,也就是不做了,那么就会有损失。如果把B 类弧割掉了,那么相应的那个仪器归到了S 集,也就是有损失。所有最小割就是使得损失最少的方案。
所以,我萌这么建边:
1.增加源点
S 和汇点T 。
2.从S 到所有实验连一条边,容量为其获利,从所有仪器到T 连一条边,容量为其花费。
3.从每个实验到相应的仪器连容量为+∞ 的边。
最后总的正的边权减去
如何输出方案?
最后一次
# include <bits/stdc++.h># define N 1010class Network {private : struct edge { int to, w, nxt ; edge ( ) { } edge ( int to, int w, int nxt ) : to ( to ), w ( w ), nxt ( nxt ) { } } g [N << 1] ; int head [N], cur [N], ecnt ; int S, T , dep [N] ; std :: bitset < N > vis ; inline int dfs ( int u, int a ) { if ( u == T || ! a ) return a ; int flow = 0, v, f ; for ( int& i = cur [u] ; i ; i = g [i].nxt ) { v = g [i].to ; if ( dep [v] == dep [u] + 1 ) { f = dfs ( v, std :: min ( g [i].w, a - flow ) ) ; g [i].w -= f, g [i ^ 1].w += f ; flow += f ; if ( a == flow ) return a ; } } if ( ! flow ) dep [u] = -1 ; return flow ; } inline bool bfs ( int S, int T ) { static std :: queue < int > q ; memset ( dep, 0, sizeof ( int ) * ( T + 1 ) ) ; vis.reset ( ) ; dep [S] = 1 ; vis [S] = 1 ; q.push ( S ) ; while ( ! q.empty ( ) ) { int u = q.front ( ) ; q.pop ( ) ; for ( int i = head [u] ; i ; i = g [i].nxt ) { int& v = g [i].to ; if ( g [i].w && ! dep [v] ) { dep [v] = dep [u] + 1 ; vis [v] = 1 ; q.push ( v ) ; } } } return dep [T] ; }public : Network ( ) { ecnt = 1 ; } inline void add_edge ( int u, int v, int w ) { g [++ ecnt] = edge ( v, w, head [u] ) ; head [u] = ecnt ; g [++ ecnt] = edge ( u, 0, head [v] ) ; head [v] = ecnt ; } inline int dinic ( int S, int T ) { this -> S = S, this -> T = T ; int rt = 0 ; while ( bfs ( S, T ) ) { memcpy ( cur, head, sizeof ( int ) * ( T + 1 ) ) ; rt += dfs ( S, 0x3f3f3f3f ) ; } return rt ; } void display ( int n, int m, int S, int T, int sum ) { int mincut = dinic ( S, T ) ; for ( int i = 1 ; i <= n ; ++ i ) if ( vis [i] ) printf ( "%d ", i ) ; puts ( "" ) ; for ( int i = n + 1 ; i <= n + m ; ++ i ) if ( vis [i] ) printf ( "%d ", i - n ) ; printf ( "\n%d\n", sum - mincut ) ; }} Lazer ;int main ( ) { int n, m ; int sum ( 0 ) ; scanf ( "%d%d", & n, & m ) ; const int S = n + m + 1, T = n + m + 2 ; for ( int i = 1 ; i <= n ; ++ i ) { int pi, id, c ; scanf ( "%d", & pi ) ; sum += pi ; Lazer.add_edge ( S, i, pi ) ; while ( ( c = getchar ( ) ) != '\n' && c != '\t' && c != '\r' ) { scanf ( "%d", & id ) ; Lazer.add_edge ( i, id + n, 0x3f3f3f3f ) ; } } for ( int i = 1 ; i <= m ; ++ i ) { int ci ; scanf ( "%d", & ci ) ; Lazer.add_edge ( i + n, T, ci ) ; } Lazer.display ( n, m, S, T, sum ) ; return 0 ;}
小结:
最大权闭合图的通用解法:
S 到正权值的点连边,容量为其权值,负权值的点到T 连边,容量为其绝对值,然后原图中的边容量为+∞ ,ans = 所有正权和 - 最小割。具体证明可以参考胡伯涛的论文。
- LOJ6001 「网络流 24 题
- loj6001「网络流 24 题」太空飞行计划(最大权闭合图+最小割)
- loj6001「网络流 24 题」太空飞行计划 最小割(最大权闭合图复习)
- 【网络流】网络流24题
- [网络流]: 网络流24题
- 网络流24题
- 网络流24题
- 网络流24题
- 网络流24题
- 「网络流 24 题」试题库
- 「网络流 24 题」试题库
- LOJ6000 「网络流 24 题
- LOJ6002 「网络流 24 题
- LOJ6003 「网络流 24 题
- LOJ6004 「网络流 24 题
- LOJ6005 「网络流 24 题
- LOJ6006「网络流 24 题
- LOJ6007 「网络流 24 题
- [ODT] Codeforces 896C. Willem, Chtholly and Seniorious
- 异常处理课堂练习2
- Maven学习笔记(四)——Maven的依赖管理
- 关于学习react之前
- 第十四周LeetCode
- LOJ6001 「网络流 24 题
- [构造] Atcoder AGC001 D. Arrays and Palindrome
- 【Scikit-Learn 中文文档】无监督学习: 寻求数据表示
- [计数 DP]Atcoder AGC001 E. BBQ Hard
- javascript闭包
- 函数重载
- 最长递增子序列
- 《我的菜谱》-西红柿炒鸡蛋
- PAX