POJ 2987 Firing(最大权闭合)

POJ 2987 Firing

题目链接

题意:n个人,每个人被炒由于都会得到一个利益(可正可负),现在有一些下属关系,如果一个人被炒了,他的下属要一起炒掉,问怎么炒使得炒利益最大,炒的人最少

思路:最大权闭合的题,要处理最少,那么其实就是在求最小割分成两个集合的时候,尽量让点都到T集合去,那么只要从S进行一次dfs,遇到满流的边就停止,经过的点都给S,就是要炒掉的人了

代码:

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;

const int MAXNODE = 5005;
const int MAXEDGE = 200005;

typedef long long Type;
const Type INF = 0x3f3f3f3f3f3f3f;

struct Edge {
	int u, v;
	Type cap, flow;
	Edge() {}
	Edge(int u, int v, Type cap, Type flow) {
		this->u = u;
		this->v = v;
		this->cap = cap;
		this->flow = flow;
	}
};

struct Dinic {
	int n, m, s, t;
	Edge edges[MAXEDGE];
	int first[MAXNODE];
	int next[MAXEDGE];
	bool vis[MAXNODE];
	Type d[MAXNODE];
	int cur[MAXNODE];
	vector<int> cut;

	void init(int n) {
		this->n = n;
		memset(first, -1, sizeof(first));
		m = 0;
	}
	void add_Edge(int u, int v, Type cap) {
		edges[m] = Edge(u, v, cap, 0);
		next[m] = first[u];
		first[u] = m++;
		edges[m] = Edge(v, u, 0, 0);
		next[m] = first[v];
		first[v] = m++;
	}

	bool bfs() {
		memset(vis, false, sizeof(vis));
		queue<int> Q;
		Q.push(s);
		d[s] = 0;
		vis[s] = true;
		while (!Q.empty()) {
			int u = Q.front(); Q.pop();
			for (int i = first[u]; i != -1; i = next[i]) {
				Edge& e = edges[i];
				if (!vis[e.v] && e.cap > e.flow) {
					vis[e.v] = true;
					d[e.v] = d[u] + 1;
					Q.push(e.v);
				}
			}
		}
		return vis[t];
	}

	Type dfs(int u, Type a) {
		if (u == t || a == 0) return a;
		Type flow = 0, f;
		for (int &i = cur[u]; i != -1; i = next[i]) {
			Edge& e = edges[i];
			if (d[u] + 1 == d[e.v] && (f = dfs(e.v, min(a, e.cap - e.flow))) > 0) {
				e.flow += f;
				edges[i^1].flow -= f;
				flow += f;
				a -= f;
				if (a == 0) break;
			}
		}
		return flow;
	}

	Type Maxflow(int s, int t) {
		this->s = s; this->t = t;
		Type flow = 0;
		while (bfs()) {
			for (int i = 0; i < n; i++)
				cur[i] = first[i];
			flow += dfs(s, INF);
		}
		return flow;
	}

	int dfs(int u) {
		vis[u] = true;
		int ans = 1;
		for (int i = first[u]; i + 1; i = next[i]) {
			int v = edges[i].v;
			if (edges[i].flow == edges[i].cap) continue;
			if (vis[v]) continue;
			ans += dfs(v);
		}
		return ans;
	}

	void solve(Type tot) {
		Type ans = Maxflow(0, n - 1);
		memset(vis, false, sizeof(vis));
		printf("%d %lld\n", dfs(0) - 1, tot - ans);
	}
} gao;

typedef long long ll;

int n, m;
ll sum;

int main() {
	while (~scanf("%d%d", &n, &m)) {
		ll w;
		sum = 0;
		gao.init(n + 2);
		for (int i = 1; i <= n; i++) {
			scanf("%lld", &w);
			if (w < 0) gao.add_Edge(i, n + 1, -w);
			if (w > 0) {
				gao.add_Edge(0, i, w);
				sum += w;
			}
		}
		int u, v;
		while (m--) {
			scanf("%d%d", &u, &v);
			gao.add_Edge(u, v, INF);
		}
		gao.solve(sum);
	}
	return 0;
}
时间: 2024-10-07 09:46:06

POJ 2987 Firing(最大权闭合)的相关文章

POJ 2987 Firing (最大权闭合图,最小割)

http://poj.org/problem?id=2987 Firing Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 7865   Accepted: 2377 Description You've finally got mad at "the world's most stupid" employees of yours and decided to do some firings. You're n

POJ 2987 Firing 最大权闭合图

点击打开链接 Firing Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 7976   Accepted: 2409 Description You've finally got mad at "the world's most stupid" employees of yours and decided to do some firings. You're now simply too mad to giv

POJ 2987 - Firing (最大权闭合子图)

题意:求一个图的最大权闭合子图 教科书:https://blog.csdn.net/can919/article/details/77603353 最小割模型的应用,源点向正权点建容量为该权值的弧;负权点向汇点建容量为该权值绝对值的弧;有偏序关系的点对之间建容量为正无穷的弧. 跑出最大流,根据最大流最小割定理:最大流的值即最小割.所求最大闭合子图的值为|正权点权值之和| - |最小割|. 此外,还要求删去人的数目.那么从源点出发,且不是最小割的边,就是被选择的边. 所以从源点dfs,若遇到容量不

poj - 2987 - Firing(最大权闭合图)

题意:n(0 < n ≤ 5000)个人,m(0 ≤ m ≤ 60000)个上下级关系,炒一个人可以获得收益或者损失bi (|bi| ≤ 10 ^ 7, 1 ≤ i ≤ n),炒一个人会把他的所有下级一起炒掉,问怎样炒人使收益最大,输出最大收益和最少炒人的数量. 题目链接:http://poj.org/problem?id=2987 -->>炒一个人会把他的所有下级一起炒掉,这时存在依赖关系,对应图论中的闭合图..最大收益对应最大权和..于是,最大权闭合图上场.. 最少炒人数?获得最大收

POJ 2987 Firing 网络流 最大权闭合图

http://poj.org/problem?id=2987 https://blog.csdn.net/u014686462/article/details/48533253 给一个闭合图,要求输出其最大权闭合图的权值和需要选的最少点数,最大权闭合图定义和网络流连边方式见博客. 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cmath> 5 #include&

poj 2987 Firing【最大权闭合子图+玄学计数 || BFS】

玄学计数 LYY Orz 第一次见这种神奇的计数方式,乍一看非常不靠谱但是仔细想想还卡不掉 就是把在建图的时候把正权变成w*10000-1,负权变成w*10000+1,跑最大权闭合子图.后面的1作用是计数,因为在最大权闭合子图中划到s点一侧的代表选,这样一来,后四位就是起了计数作用.sum初始统计的个数就是所有正权点,然后dinic中割掉一个正权点的边即相当于在最终答案的后四位+1,也就是点数-1 然后考虑收益相同的方案,点数多的后四位一定小,而当前求得又是最小割,所以会选割掉点数少的,也就是留

poj Firing(最大权闭合图)

Firing 题目: 要解雇一些人,而解雇的这些人如果人跟他有上下级的关系,则跟他有关系的人也要一起解雇.每个人都会创造一定的价值,要求你求出在最大的获利下,解雇的人最小. 算法分析: 在这之前要知道一个定理: 最小割 = 最大流 一道最大权闭合图的裸题,而这又可以转换成最小割来求解.证明可以看2007年胡伯涛的论文则可以直接套出模板,没看过的最好去看一下,那里解释的清楚.这里我给出他的原文的一些构造方法. 增加源s汇t 源s连接原图的正权点,容量为相应点权 原图的负权点连接汇t,容量为相应点权

POJ 2987 Firing

Firing Time Limit: 5000ms Memory Limit: 131072KB This problem will be judged on PKU. Original ID: 298764-bit integer IO format: %lld      Java class name: Main You’ve finally got mad at “the world’s most stupid” employees of yours and decided to do s

POJ 2987 Firing (最大权闭合图)

Firing Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 12108   Accepted: 3666 Description You've finally got mad at "the world's most stupid" employees of yours and decided to do some firings. You're now simply too mad to give resp