[最大权闭合子图小练]

[POJ 2987]每个人都为公司带来一个收益,公司老板要裁员(当然是业绩不好给公司带来负收益的人被裁掉啦~),然而踢走一个人会把他的下属都踢走,求最大收益

 

最大权闭合子图~,其实就是最小割啦,此题要求最小化走的人数,然而就是最小割中的人数QAQ(并不知道为什么),dfs(S)相关的点

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define maxn 100010
using namespace std;

typedef long long ll;

const int inf = 0x7fffffff;

int n, m;

struct Edge{
	int to, next, w;
	Edge(int v = 0, int nxt = 0, int d = 0){
		to = v, next = nxt, w = d;
	}
}edge[maxn * 5];
int h[maxn], cnt = 1, S, T;

void add(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;
}

queue<int> Q;

int d[70000];

bool BFS(){
	memset(d, -1, sizeof d);
	d[S] = 0; Q.push(S);
	while(!Q.empty()){
		int u = Q.front(); Q.pop();
		for(int i = h[u]; i; i = edge[i].next){
			if(!edge[i].w)continue;
			int v = edge[i].to;
			if(d[v] == -1) d[v] = d[u] + 1, Q.push(v);
		}
	}return d[T] != -1;
}

int cur[maxn];

ll DFS(int x, ll a){
	if(x == T || a == 0)
	    return a;
	int used = 0, f;
	for(int i = cur[x]; i; i = edge[i].next){
		int v = edge[i].to;
		if(d[v] == d[x] + 1 && edge[i].w){
			f = DFS(v, min(a-used, (ll)edge[i].w));
			edge[i].w -= f;
			edge[i^1].w += f;
			used += f;
			if(f > 0)cur[x] = i;
			if( used == a)return used;
		}
	}
	if(used == 0)d[x] = -1;
	return used;
}

ll Dinic(){
	ll ret = 0;
	while(BFS()){
		for(int i = S; i <= T; i ++)
		    cur[i] = h[i];
		ret += DFS(S, inf);
	}
	return ret ;
}

bool vis[maxn];

void dfs(int u){
	vis[u] = true;
	for(int i = h[u]; i; i = edge[i].next){
		int v = edge[i].to;
		if(edge[i].w && !vis[v])
		    dfs(v);
	}
}

int main(){
	scanf("%d%d", &n, &m);
	S = 0, T = n + m + 1;
	int b;
	
	ll sum = 0;
 	for(int i = 1; i <= n; i ++){
		scanf("%d", &b);
		if(b > 0){
			add(S, i, b);
			sum += b;
		}
		else add(i, T, -b);
 	}
 	int u, v;
 	for(int i = 1; i <= m; i ++){
		scanf("%d%d", &u, &v);
		add(u, v, inf);
 	}
 	sum -= Dinic();
 	dfs(S);
 	int ans = 0;
 	for(int i = 1; i <= n; i ++)
 	    ans += vis[i];
 	printf("%d %lld\n", ans, sum);
	return 0;
}

[HDU 3879]建立一个新站点需要Pi的花费,给定m个客户使用两个站点所带来的收益Ci,最大化收益

 

把每一个客户新建一个点然后建图QAQ,不能在原图上直接连边。。只贴建图了QAQ。。

while(scanf("%d%d", &n, &m) == 2){
	memset(h, 0, sizeof h); cnt = 1;
	int p; S = 0, T = n + m + 1;
	for(int i = 1; i <= n; i ++){
		scanf("%d", &p);
		add(S, i, p);
	}
	int u, v, w, sum = 0, size = n;
	for(int i = 1; i <= m; i ++){
		scanf("%d%d%d", &u, &v, &w);
		++ size;
		add(u, size, inf);
		add(v, size, inf);
		add(size, T, w);
		sum += w;
	}
	printf("%d\n", sum - Dinic());
}

[HDU 4971]

The first line of the input is a single integer T(<=100) which is the number of test cases.

Each test case contains a line with two integer n(<=20) and m(<=50) which is the number of project to select to complete and the number of technical problem.

Then a line with n integers. The i-th integer(<=1000) means the profit of complete the i-th project.

Then a line with m integers. The i-th integer(<=1000) means the cost of training to solve the i-th technical problem.

Then n lines. Each line contains some integers. The first integer k is the number of technical problems, followed by k integers implying the technical problems need to solve for the i-th project.

After that, there are m lines with each line contains m integers. If the i-th row of the j-th column is 1, it means that you need to solve the i-th problem before solve the j-th problem. Otherwise the i-th row of the j-th column is 0.

 

就是有问题和工程,解决问题是有关联性的而且要花费代价,解决一些问题可以去做某工程带来一些收益,求最大化收益

 

最重要的是建图啦。。

for(int dir = 1; dir <= test; dir ++){
	memset(h, 0, sizeof h);
	int sum = 0, p, k;
	scanf("%d%d", &n, &m);
	cnt = 1, S = 0, T = n + m + 1;
	for(int i = 1; i <= n; i ++){
		scanf("%d", &p);
		add(S, i, p);
		sum += p;
	}

	for(int i = 1; i <= m; i ++){
		scanf("%d", &p);
		add(i + n, T, p);
	}

	for(int i = 1; i <= n; i ++){
		scanf("%d", &k);
		for(int j = 1; j <= k; j ++){
			scanf("%d", &p); p ++;
			add(i, p + n, inf);
		}
	}

	for(int i = 1; i <= m; i ++){
		for(int j = 1; j <= m; j ++){
			scanf("%d", &p);
			if(p == 1) add(i + n, j + n, inf);
		}
	}
	printf("Case #%d: %d\n", dir, sum - Dinic());
}

vfk once said

我们来回忆一下最小割。如果v向u连一条容量为w的有向边,表示v如果在S割,那么u不在S割会产生w的代价。
一个等价的表述是,u如果在T割,那么v不在T割会产生w的代价。
注意v如果在T割,那么u在S割是不会产生代价的。
特别的,如果v向u连一条容量为正无穷大的有向边,表示v如果在S割,那么u一定也要在S割。
一个等价的表述是,u如果在T割,那么v一定也要在T割。
 
总结一下就是选择一个点可以选或者必须选某个点,可以用最大权闭合子图来解决。
posted @ 2016-04-03 09:13  _Horizon  阅读(169)  评论(0编辑  收藏  举报