1. 程式人生 > >hiho# 1398 最大權閉合子圖 網絡流

hiho# 1398 最大權閉合子圖 網絡流

type scanf 傳送門 memset size spa http 二分圖 ==

題目傳送門

題意:給出n個活動,m個人,請人需要花費$a[i]$的錢,舉辦一次活動可以賺$b[i]$的錢,但是需要固定的幾個人在場,一個人只需要請一次後就必定在場,問最大收益。

思路:

  下列結論來自hihocoder的例題

  下面不加證明的給出幾個概念和結論。

  1)閉合子圖:給定一個有向圖,從中選擇一些點組成一個點集V。對於V中任意一個點,其後續節點都仍然在V中。比如:

  技術分享圖片

在這個圖中有8個閉合子圖:∅,{3},{4},{2,4},{3,4},{1,3,4},{2,3,4},{1,2,3,4}

  2) 最大權閉合子圖:如上圖的二分圖,A部權值為正,B部權值為負,要求閉合子圖權值最大,即為最大權閉合子圖。

  3)最大權閉合子圖求法:首先建立源點s和匯點t,將源點s與所有權值為正的點相連,容量為權值;將所有權值為負的點與匯點t相連,容量為權值的絕對值;權值為0的點不做處理;同時將原來的邊容量設置為無窮大。$ans=權值為正的點的和-最小割$

此題顯然就是求一個最大權閉合子圖。

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;

typedef long long ll;

const ll INFLL = 0x3f3f3f3f3f3f3f3f;
const
int INF = 0x3f3f3f3f; const int maxn = 510; struct Edge { int to, flow, nxt; Edge(){} Edge(int to, int nxt, int flow):to(to),nxt(nxt), flow(flow){} }edge[maxn * maxn * 2]; int head[maxn*2], dep[maxn*2]; int S, T; int N, n, m, tot; void init(int n) { N=n; for (int i = 0
; i <= N; ++i) head[i] = -1; tot = 0; } void addv(int u, int v, int w, int rw = 0) { edge[tot] = Edge(v, head[u], w); head[u] = tot++; edge[tot] = Edge(u, head[v], rw); head[v] = tot++; } bool BFS() { for (int i = 0; i <= N; ++i) dep[i] = -1; queue<int>q; q.push(S); dep[S] = 1; while (!q.empty()) { int u = q.front(); q.pop(); for (int i = head[u]; ~i; i = edge[i].nxt) { if (edge[i].flow && dep[edge[i].to] == -1) { dep[edge[i].to] = dep[u] + 1; q.push(edge[i].to); } } } return dep[T] < 0 ? 0 : 1; } int DFS(int u, int f) { if (u == T || f == 0) return f; int w, used = 0; for (int i = head[u]; ~i; i = edge[i].nxt) { if (edge[i].flow && dep[edge[i].to] == dep[u] + 1) { w = DFS(edge[i].to, min(f - used, edge[i].flow)); edge[i].flow -= w; edge[i ^ 1].flow += w; used += w; if (used == f) return f; } } if (!used) dep[u] = -1; return used; } int Dicnic() { int ans = 0; while (BFS()) { ans += DFS(S, INF); } return ans; } int main(){ cin>>n>>m; T=n+m+1; init(T); S=0; for(int i=1;i<=m;i++){ int w; scanf("%d",&w); addv(i+n,T,w); } int res=0; for(int i=1;i<=n;i++){ int w,k; scanf("%d%d",&w,&k); addv(S,i,w); res+=w; while(k--){ scanf("%d",&w); addv(i,n+w,INF); } } int ans=res-Dicnic(); printf("%d\n",ans); }

hiho# 1398 最大權閉合子圖 網絡流