一看到这个题就知道是最大闭合子图了,关于最大闭合子图的定义和建图,都是看胡伯涛的论文上的。这里有两问,首先是要求闭合图权最大,第二问,也是比较有难度的就是在闭合图权最大的条件下,尽量多的让dream所代表的点出现在最大权闭合图的闭合图中。最大闭合图是最小割模型的一个应用。由于一个流网络的最小割可能不唯一,所以最大闭合图也不一定是唯一的,所以第二问的难度就增加了。
这里的解决方法是借助偏移量来维护两个量,一个是最大流,一个是用到的点数。由题意可得,图中点数最多将达到2002,那么现在选择一个MOD=3000的偏移量。首先要统计图中所有点权为正的点的权和sum。建图的时候,对于每个价值为value的dream,从源点S到dream点连一条流量cap=MOD*value+1的边。这里的+1就表示我选了这个点了,如果这条边在最小割中的话,这个+1就一定会在最后体现出来。对于每个价值为value的付出(用点i表示),若value>=0,加边(S,i,value*MOD);value<0,加边(i,T,-value*MOD)。对原图中所有的有向边,加边(i,j,INF)。跑最大流,得到maxflow。那么根据最大闭合子图的解法,最大闭合子图权=sum-maxflow/MOD,可以完成的梦想=总梦想数n-maxflow%MOD;
命题人ZZY的灵感来自于POJ2987,也是一道最大闭合子图的题目。他的题解在这里:http://blog.csdn.net/kk303/article/details/11843169
至于他的那个建图为什么是对的,或者说可以过,他和我都晕了。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 typedef long long LL; 7 #define INF ((LL)2000000007)*((LL)2000000007) 8 #define maxn 2010 9 #define maxm 200000 10 #define MOD 3000 11 int u[maxm],v[maxm],next[maxm]; 12 LL w[maxm]; 13 int first[maxn],d[maxn],work[maxn],q[maxn]; 14 int e,S,T; 15 int n,m; 16 17 void init(){ 18 memset(first,-1,sizeof(first)); 19 e = 0; 20 } 21 22 void add_edge(int a,int b,LL c){ 23 //printf("add:from %d to %d,cap = %lld ",a,b,c); 24 u[e] = a;v[e] = b;next[e] = first[a];w[e] = c;first[a] = e++; 25 u[e] = b;v[e] = a;next[e] = first[b];w[e] = 0;first[b] = e++; 26 } 27 28 int bfs(){ 29 int rear = 0; 30 memset(d,-1,sizeof(d)); 31 d[S] = 0;q[rear++] = S; 32 for(int i = 0;i < rear;i++){ 33 for(int j = first[q[i]];j != -1;j = next[j]) 34 if(w[j] && d[v[j]] == -1){ 35 d[v[j]] = d[q[i]] + 1; 36 q[rear++] = v[j]; 37 if(v[j] == T) return 1; 38 } 39 } 40 return 0; 41 } 42 43 LL dfs(int cur,LL a){ 44 if(cur == T) return a; 45 for(int &i = work[cur];i != -1;i = next[i]) 46 if(w[i] && d[v[i]] == d[cur] + 1) 47 if(LL t = dfs(v[i],min(a,w[i]))){ 48 w[i] -= t;w[i^1] += t; 49 return t; 50 } 51 return 0; 52 } 53 54 LL dinic(){ 55 LL ans = 0; 56 while(bfs()){ 57 memcpy(work,first,sizeof(first)); 58 while(LL t = dfs(S,INF)) ans += t; 59 } 60 return ans; 61 } 62 63 int main() 64 { 65 while(scanf("%d%d",&n,&m) != EOF){ 66 init(); 67 S = 0,T = n+m+1; 68 int sum = 0; 69 for(int i = 1;i <= n;i++){ 70 int value; 71 scanf("%d",&value); 72 add_edge(S,i,value*MOD + 1); 73 sum += value; 74 } 75 for(int i = 1;i <= m;i++){ 76 int value; 77 scanf("%d",&value); 78 if(value >= 0) add_edge(S,i+n,value*MOD),sum += value; 79 else add_edge(i+n,T,-value*MOD); 80 } 81 for(int i = 1;i <= n;i++){ 82 int t; 83 scanf("%d",&t); 84 while(t--){ 85 int tmp; 86 scanf("%d",&tmp); 87 add_edge(i,n + tmp,INF); 88 } 89 } 90 LL maxflow = dinic(); 91 //printf("maxflow = %lld ",maxflow); 92 printf("%lld %lld ",sum - maxflow/MOD,n - maxflow%MOD); 93 } 94 return 0; 95 } 96 97 /************************************************************** 98 Problem: 1319 99 User: 1234 100 Language: C++ 101 Result: Accepted 102 Time:164 ms 103 Memory:5420 kb 104 ****************************************************************/