POJ 2125 Destroying The Graph(二分图最小点权覆盖)
2015-09-03 20:18
417 查看
题目大意:给出一个有向图。对于每个点,定义两种操作:
操作A:删除点u的所有出边,花费为Cu
操作B:删除点u的所有入边,花费为du
要使所有的边都被删除,最小的花费是多少,并输出所有操作
解题思路:一看就知道是二分图的最小点权覆盖了,二分图的最小点权覆盖,胡伯涛:算法合集之《最小割模型在信息学竞赛中å的应用》 里面有讲,里面的内容挺重要的,推荐自己理解
这里讲一下如果找出所有的操作
首先,先dfs找出S集的点
接着判断一下哪些边是割边,割边就是我们所要的操作了(因为割边将所有的s-u-v-t的边全部破坏掉了,且花费的代价是最小的)
如何判断是否是割边呢?前面我们dfs的时候已经标记出了所有属于S集合的点了,而那些没标记的点肯定是T集的点了,所以判断一条边是不是割边的依据就是该边的起点是属于S集的,终点是属于T集的,且容量-流量==0
操作A:删除点u的所有出边,花费为Cu
操作B:删除点u的所有入边,花费为du
要使所有的边都被删除,最小的花费是多少,并输出所有操作
解题思路:一看就知道是二分图的最小点权覆盖了,二分图的最小点权覆盖,胡伯涛:算法合集之《最小割模型在信息学竞赛中å的应用》 里面有讲,里面的内容挺重要的,推荐自己理解
这里讲一下如果找出所有的操作
首先,先dfs找出S集的点
接着判断一下哪些边是割边,割边就是我们所要的操作了(因为割边将所有的s-u-v-t的边全部破坏掉了,且花费的代价是最小的)
如何判断是否是割边呢?前面我们dfs的时候已经标记出了所有属于S集合的点了,而那些没标记的点肯定是T集的点了,所以判断一条边是不是割边的依据就是该边的起点是属于S集的,终点是属于T集的,且容量-流量==0
[code]#include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <queue> using namespace std; #define N 1010 #define INF 0x3f3f3f3f struct Edge{ int from, to, cap, flow; Edge() {} Edge(int from, int to, int cap, int flow) : from(from), to(to), cap(cap), flow(flow) {} }; struct Dinic{ int n, m, s, t; vector<Edge> edges; vector<int> G ; bool vis ; int d , cur ; void init(int n) { this->n = n; for (int i = 0; i <= n; i++) { G[i].clear(); } edges.clear(); } void AddEdge(int from, int to, int cap) { edges.push_back(Edge(from, to, cap, 0)); edges.push_back(Edge(to, from, 0, 0)); int m = edges.size(); G[from].push_back(m - 2); G[to].push_back(m - 1); } bool BFS() { memset(vis, 0, sizeof(vis)); queue<int> Q; Q.push(s); vis[s] = 1; d[s] = 0; while (!Q.empty()) { int u = Q.front(); Q.pop(); for (int i = 0; i < G[u].size(); i++) { Edge &e = edges[G[u][i]]; if (!vis[e.to] && e.cap > e.flow) { vis[e.to] = true; d[e.to] = d[u] + 1; Q.push(e.to); } } } return vis[t]; } int DFS(int x, int a) { if (x == t || a == 0) return a; int flow = 0, f; for (int i = cur[x]; i < G[x].size(); i++) { Edge &e = edges[G[x][i]]; if (d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap - e.flow))) > 0) { e.flow += f; edges[G[x][i] ^ 1].flow -= f; flow += f; a -= f; if (a == 0) break; } } return flow; } int Maxflow(int s, int t) { this->s = s; this->t = t; int flow = 0; while (BFS()) { memset(cur, 0, sizeof(cur)); flow += DFS(s, INF); } return flow; } }; Dinic dinic; #define maxn 110 int n, m, source, sink, cnt; int in[maxn], out[maxn], ans ; bool vis[maxn * 2]; void init() { source = 0, sink = 2 * n + 1; dinic.init(sink); int c; for (int i = 1; i <= n; i++) { scanf("%d", &c); dinic.AddEdge(i + n, sink, c); } for (int i = 1; i <= n; i++) { scanf("%d", &c); dinic.AddEdge(source, i, c); } int u, v; for (int i = 1; i <= m; i++) { scanf("%d%d", &u, &v); dinic.AddEdge(u, n + v, INF); } } void dfs(int u) { vis[u] = true; for (int i = 0; i < dinic.G[u].size(); i++) { Edge &e = dinic.edges[dinic.G[u][i]]; if (!vis[e.to] && e.cap - e.flow > 0) dfs(e.to); } } void solve() { int maxflow = dinic.Maxflow(source, sink); memset(vis, 0, sizeof(vis)); cnt = 0; dfs(source); for (int i = 0; i < 4 * n; i += 2) { Edge &e = dinic.edges[i]; int u = e.from, v = e.to; if (vis[u] && !vis[v] && e.flow - e.cap == 0) ans[cnt++] = i; } printf("%d\n%d\n", maxflow, cnt); for (int i = 0; i < cnt; i++) { int u = dinic.edges[ans[i]].from, v = dinic.edges[ans[i]].to; if (u == source) printf("%d -\n", v); else printf("%d +\n", u - n); } } int main() { while (scanf("%d%d", &n, &m) != EOF) { init(); solve(); } return 0; }
相关文章推荐
- 初始化和清除
- hdoj 2095 find your present (2)【位运算,异或】
- 【codechef】Singal Pipes(DFS求期望)
- 实现iOS 8 Safari可伸缩的NavigationBar
- 1.2 二分法
- Java中多线程重复启动
- UVa 10723 LCS变形 Cyborg Genes
- Ruby on Rails Guides(一)
- hdu 5417 Victor and Machine
- Nginx中如何限制某个IP同一时间段的访问次数
- 文件查找命令 whereis locate find
- Linux套接字与虚拟文件系统(1):初始化和创建 http://www.cppblog.com/qinqing1984/archive/2015/05/03/210521.html
- 使用Ajax加载数据的dataTables
- 20150902 Java学习笔记-构造方法,关键字,封装性
- shutdown immediate 卡在SMON: disabling tx recovery
- 树的重心 树形DP SGU 134
- C++模板元编程 - 3 逻辑结构,递归,一点列表的零碎,一点SFINAE
- hdoj 4686 Arc of Dream 【矩阵快速幂】
- 如何写memset函数
- GCD的基本使用