POJ 2987 最大權閉合圖
阿新 • • 發佈:2019-01-01
//poj2987 //最大權閉合圖加最少點數 //最少點數我就先忽視了 莫名奇妙的過了。。 總之題目不簡單,但資料很水。。 請參考胡伯濤的論文《最小割模型在資訊學競賽中的應用》 閉合圖的概念就很好引出了。在一個圖中,我們選取一些點構成集合,記為V,且集合中的出邊(即集合中的點的向外連出的弧),所指向的終點(弧頭)也在V中,則我們稱V為閉合圖。最大權閉合圖即在所有閉合圖中,集合中點的權值之和最大的V,我們稱V為最大權閉合圖。 首先引入結論,最小割所產生的兩個集合中,其源點S所在集合(除去S)為最大權閉合圖,接下來我們來說明一些結論。 證明:最小割為簡單割。 引入一下簡單割的概念:割集的每條邊都與S或T關聯。(請下面閱讀時一定分清最小割與簡單割,容易混淆) 那麼為什麼最小割是簡單割呢?因為除S和T之外的點間的邊的容量是正無窮,最小割的容量不可能為正無窮。所以,得證。 證明網路中的簡單割與原圖中閉合圖存在一一對應的關係。(即所有閉合圖都是簡單割,簡單割也必定是一個閉合圖)。 證明閉合圖是簡單割:如果閉合圖不是簡單割(反證法)。那麼說明有一條邊是容量為正無窮的邊,則說明閉合圖中有一條出邊的終點不在閉合圖中,矛盾。 證明簡單割是閉合圖:因為簡單割不含正無窮的邊,所以不含有連向另一個集合(除T)的點,所以其出邊的終點都在簡單割中,滿足閉合圖定義。得正。 證明最小割所產生的兩個集合中,其源點S所在集合(除去S)為最大權閉合圖。 首先我們記一個簡單割的容量為C,且S所在集合為N,T所在集合為M。 則C=M中所有權值為正的點的權值(即S與M中點相連的邊的容量)+N中所有權值為負的點權值的絕對值(即N中點與T中點相連邊的容量)。記(C=x1+y1);(很好理解,不理解畫一個圖或想象一下就明白了)。 我們記N這個閉合圖的權值和為W。 則W=N中權值為正的點的權值-N中權值為負的點的權值的絕對值。記(W=x2-y2); 則W+C=x1+y1+x2-y2。 因為明顯y1=y2,所以W+C=x1+x2; x1為M中所有權值為正的點的權值,x2為N中權值為正的點的權值。 所以x1+x2=所有權值為正的點的權值之和(記為TOT). 所以我們得到W+C=TOT.整理一下W=TOT-C. 到這裡我們就得到了閉合圖的權值與簡單割的容量的關係。 因為TOT為定值,所以我們欲使W最大,即C最小,即此時這個簡單割為最小割,此時閉合圖為其源點S所在集合(除去S)。得正。 至此,我們就將最大權閉合圖問題轉化為了求最小割的問題。求最小割用最小割容量=最大流,即可將問題轉化為求最大流的問題。 ----------------------------------------------------------- 然後再說本題 本題還有一個要求就是要求不僅要是最大權,並且要求點數還最少 看一個神牛的證明----http://hi.baidu.com/dispossessed/blog/item/2396c0ddbc73a2caa044df44.html 下面證明最小割對應取點方案就是最小取點數 由於原圖是個DAG圖,所以對於取得的最大權閉合圖K,取它的任意一個子圖G,如果從K-G仍然是一個閉合圖,那麼的點權和一定大於等於0,例如:1->2,2->3,1->4,4->5,若最大權閉合圖為:{1,2,3,4,5},那麼其中任一滿足條件的G({1},{1,2},{1,2,3},{1,4},{1,4,5})點權和一定大於等於0,否則去除G,K-G仍然為閉合圖,但是K-G的點權和會大於K 所以如果有兩種取點方式使得權值相同,但是取點數不同的話,那麼肯定存在一個可以移除的滿足條件的子圖G,其點權和為0 下面考慮構造的網路,對於G在網路中的對應圖G',由於在網路求的是最小割,即最大流,而且G的點權和為0,所以G'中與源點S連邊的容量和等於G'中與匯點T連邊的流量和,同時由於去除G後K還是一個閉合圖,所以只有可能G'中的流量流入K'-G',不可能有流量從K'-G'流入G',所以G'的邊中除了流量為inf的那些,一定是滿流的 再考慮在殘留網路中求出取點集的方法,從源點開始floodfill,忽略滿流邊,即殘留網路中的0流邊,可以遍歷到的點就是要取的點集了,這個道理想一下簡單割和閉合圖的取法一一對應就可以了 那麼G'既然是滿流的,在殘留網路中就不可能對這些0流邊進行處理,那就不會取到G中的點進入取點集,所以建立網路求得得最小割對應的取法取出的就是最小的點數了 -------------------------------- 當然還有一種是神奇的放大邊權方法 建圖前,對所有b[i],執行變換b[i]=b[i]*10000-1,然後,會驚異地發現, 此時最大流所對應的方案就是滿足辭退最少人數的了。 為什麼?顯然,變換後的流量r2除以10000後再取整就等於原來的流量,但是 r2的後四位卻蘊含了辭退人數的資訊:每多辭退一個人,流量就會少1。 想法還是又妙又多的,要搞清晰 首先他的最小割是唯一的,然後他證明了如何在這個最小割裡找一個最少的點數。 比如如果後四位是9998,則裁的為2個人。 #include<iostream> #include<cstring> #include<string> #include<cstdlib> #include<cstdio> #include<vector> #include<queue> #define ll __int64 #define maxn 6000 #define INF 0x3f3f3f3f using namespace std; ll n,m; ll tempa,tempb; ll w[maxn]; ll tot; bool visit[maxn]; struct Dinic { ll edgeNum,source,sink; bool vis[maxn]; ll d[maxn]; ll cur[maxn]; ll mark[maxn]; struct edge { ll from,to,cap,flow; }; vector<edge> edges; vector<ll> mapt[maxn]; void addEdge(ll from,ll to,ll cap){ //pf("%d %d %d\n",from,to,cap); edges.push_back((edge){from,to,cap,0}); edges.push_back((edge){to,from,0,0}); edgeNum = edges.size(); mapt[from].push_back(edgeNum-2); mapt[to].push_back(edgeNum-1); } bool bfs() { memset(vis,0,sizeof vis); queue<ll> q; q.push(source); d[source] =0; vis[source] =1; while(!q.empty()){ ll x = q.front(); q.pop(); for(ll i=0;i<mapt[x].size();i++){ edge& e = edges[mapt[x][i]]; if(!vis[e.to]&&e.cap>e.flow){ vis[e.to] = 1; d[e.to] = d[x] +1; q.push(e.to); } } } return vis[sink]; } ll dfs(ll x,ll a){ if(x==sink||a==0) return a; ll flow=0,f; for(ll& i=cur[x];i<mapt[x].size();i++){ edge& e = edges[mapt[x][i]]; if(d[x]+1==d[e.to]&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0){ e.flow += f; edges[mapt[x][i]^1].flow -= f; flow += f; a -= f; if(a==0) break; } } return flow; } ll maxFlow(ll source,ll sink){ this->sink = sink; this->source = source; ll flow = 0; while(bfs()){ memset(cur,0,sizeof cur); flow += dfs(source,INF); } return flow; } void dfsans(ll x) { visit[x]=true; for(ll i=0;i<mapt[x].size();i++) { edge& e = edges[mapt[x][i]]; if((e.cap-e.flow)>0) { ll temp=e.to; if(!visit[temp]) { dfsans(temp); tot++; } } } } }; int main() { while(scanf("%I64d%I64d",&n,&m)!=EOF) { Dinic dinic; ll ans=0; ll s=0;ll dest=n+1; for(ll i=1;i<=n;i++) { scanf("%I64d",&w[i]); if(w[i]>0) { dinic.addEdge(s,i,w[i]); ans+=w[i]; } else dinic.addEdge(i,dest,-w[i]); } for(ll i=1;i<=m;i++) { scanf("%I64d%I64d",&tempa,&tempb); dinic.addEdge(tempa,tempb,INF); } ans=ans-dinic.maxFlow(s,dest); memset(visit,false,sizeof(visit)); tot=0; dinic.dfsans(s); cout<<tot<<" "<<ans<<endl; } }