最大權閉合子圖_洛谷3410

題目:
題目描述
小B有n個下屬,現小B要帶着一些下屬讓別人拍照。

有m個人,每個人都願意付給小B一定錢讓n個人中的一些人進行合影。如果這一些人沒帶齊那麼就不能拍照,小B也不會得到錢。

注意:帶下屬不是白帶的!!!對於每個下屬,如果他帶了那麼小B需要給他一些錢,保證當他拍照時配合。

請問,小B的淨收益最多是多少。

輸入格式
第1行有2個正整數m和n(0<m,n<=100)。接下來的m行,每行是一個要求拍照的人的有關數據。第一個數是他同意支付該合影的費用;接着是該合影需要的若干下屬的編號,以一個0作爲行的結束標記。最後一行的n個數是帶每個下屬的費用。

輸出格式
一個數,表示最大收益。小B可以一個人也不帶。

輸入輸出樣例
輸入 #1 複製
2 3
10 1 2 0
25 2 3 0
5 6 7
輸出 #1 複製
17
說明/提示
對於10%的數據每個人都要求讓全部n個人合影

對於30%的數據n<=15 m<=15

另有10%的數據答案爲0

對於50%的數據n<=40 m<=40

另有10%的數據每個人只願意拍一個人

對於100%的數據m,n<=100

最大權閉合子圖:
有一個有向圖,每一個點都有一個權值(可以爲正或負或0),選擇一個權值和最大的子圖,使得每個點的後繼都在子圖裏面,這個子圖就叫最大權閉合子圖。

解題思路1:
有一些點,有些點的權值是正的有一些點的權值是負數,然後所有的正的點都指向一些權值是負的點,這道題讓我們去得到收益的最大值,也就是讓我們去找到一個最大權閉合子圖,求出這個子圖的收益之和。所以我們可以讓s 指向m個點,邊權分別爲對應的收益,然後這m個然分別指向對應的n個人中的一些人,邊權爲inf, 最後讓這個n個人指向t,邊權爲這n個人的花費,最後 答案 ans = 有效的收益 - 最小割。

解題思路(相當於解題思路一中結論的證明):
首先構圖和上面的想法是一樣的,然後我們不妨這樣想,我們需要在A地和B地裏種菜,這些菜要麼只能在A地裏獲得收益,要麼只能在B地裏獲得收益,然後我們去求這個圖的最小割,即用最小的代價讓s 和 t 不連通。那麼我們最後 割下的邊要麼是題目中正收益的邊,要麼是負收益的邊;被割下的正收益的邊表示我選這條邊反而會是我的收益下降所以割下了,割下的負收益的邊表示這些邊是我選取正收益的邊本來就應該支付的代價,那麼用我們的全部的正收益之和 - 最小的代價和 就是我們想要得到的答案。

注意點:
1.記得給flow 賦值,即while(flow = dinic(s, inf)) 。
AC代碼:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>

using namespace std;

const int N = 300, M = 30000;
const int inf = 0x3f3f3f3f;

int n, m, ans, maxflow, s, t;
int h[N], w[M], e[M], ne[M], now[M], idx;
int d[N];

inline void add(int a, int b, int c) {
	e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
	e[idx] = a, w[idx] = 0, ne[idx] = h[b], h[b] = idx ++;
}

inline bool bfs(void) {
	queue<int> q;
	while(q.size()) q.pop();
	memset(d, 0, sizeof d);
	
	d[s] = 1; q.push(s);
	
	while(q.size()) {
		int u = q.front(); q.pop();
		
		for(int i = h[u]; i != -1; i = ne[i]) {
			int v = e[i];
			if(w[i] && !d[v]) {
				d[v] = d[u] + 1;
				q.push(v);
			}
		}
	}
	
	if(d[t] == 0) return false;
	return true;	
	
}

inline int dinic(int u, int flow) {
	int i;
	
	if(u == t) return flow;
	
	for(i = now[u]; i != -1; i = ne[i]) {
		int v = e[i];
		if(d[v] == d[u] + 1 && w[i]) {
			int k = dinic(v, min(flow, w[i]));
			if(k) {
				w[i] -= k;
				w[i ^ 1] += k;
				return k;
			} else d[v] = 0;
		}
		
		now[u] = i;
	}
	
	return 0;
}

int main(void) {
//	freopen("in.txt", "r", stdin);
	
	scanf("%d%d", &m, &n);
	memset(h, -1, sizeof h);
	s = m + n + 1, t = s + 1;
	
	for(int i = 1; i <= m; i ++) {
		int cost; int b;
		scanf("%d", &cost); ans += cost;
		add(s, i, cost);
		
		while(scanf("%d", &b) != EOF && b) {
			add(i, m + b, inf);
		}
	}
	
	for(int i = 1; i <= n; i ++) {
		int cost; scanf("%d", &cost);
		add(i + m, t, cost);
	}
	
	int flow;
	while(bfs()) {
		memcpy(now, h, sizeof h);
		while(flow = dinic(s, inf)) maxflow += flow;
	}
	
	ans -= maxflow;
	
	printf("%d\n", ans);
	
//	fclose(stdin);
	return 0;
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章