POJ 2987 - Firing (最大權閉合子圖)
阿新 • • 發佈:2018-09-14
detail void int ret 此外 con ini 就是 memset
題意:求一個圖的最大權閉合子圖
教科書:https://blog.csdn.net/can919/article/details/77603353
最小割模型的應用,源點向正權點建容量為該權值的弧;負權點向匯點建容量為該權值絕對值的弧;有偏序關系的點對之間建容量為正無窮的弧.
跑出最大流,根據最大流最小割定理:最大流的值即最小割.所求最大閉合子圖的值為|正權點權值之和| - |最小割|.
此外,還要求刪去人的數目.那麽從源點出發,且不是最小割的邊,就是被選擇的邊.
所以從源點dfs,若遇到容量不為0的邊則結果++.最後記得減去源點
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> using namespace std; const int MAXN=6010;//點數的最大值 const int MAXM=100010;//邊數的最大值 #define captype long long typedef long long LL; const LL INF = (1LL)<<60; struct SAP_MaxFlow{ struct EDGE{ int to,next; captype cap; }edg[MAXM]; int eid,head[MAXN]; int gap[MAXN]; int dis[MAXN]; int cur[MAXN]; int pre[MAXN]; void init(){ eid=0; memset(head,-1,sizeof(head)); } void AddEdge(int u,int v,captype c,captype rc=0){ edg[eid].to=v; edg[eid].next=head[u]; edg[eid].cap=c; head[u]=eid++; edg[eid].to=u; edg[eid].next=head[v]; edg[eid].cap=rc; head[v]=eid++; } captype maxFlow_sap(int sNode,int eNode, int n){//n是包括源點和匯點的總點個數,這個一定要註意 memset(gap,0,sizeof(gap)); memset(dis,0,sizeof(dis)); memcpy(cur,head,sizeof(head)); pre[sNode] = -1; gap[0]=n; captype ans=0; int u=sNode; while(dis[sNode]<n){ if(u==eNode){ captype Min=INF ; int inser; for(int i=pre[u]; i!=-1; i=pre[edg[i^1].to]) if(Min>edg[i].cap){ Min=edg[i].cap; inser=i; } for(int i=pre[u]; i!=-1; i=pre[edg[i^1].to]){ edg[i].cap-=Min; edg[i^1].cap+=Min; } ans+=Min; u=edg[inser^1].to; continue; } bool flag = false; int v; for(int i=cur[u]; i!=-1; i=edg[i].next){ v=edg[i].to; if(edg[i].cap>0 && dis[u]==dis[v]+1){ flag=true; cur[u]=pre[v]=i; break; } } if(flag){ u=v; continue; } int Mind= n; for(int i=head[u]; i!=-1; i=edg[i].next) if(edg[i].cap>0 && Mind>dis[edg[i].to]){ Mind=dis[edg[i].to]; cur[u]=i; } gap[dis[u]]--; if(gap[dis[u]]==0) return ans; dis[u]=Mind+1; gap[dis[u]]++; if(u!=sNode) u=edg[pre[u]^1].to; //退一條邊 } return ans; } }F; bool vis[MAXN]; int tot = 0; void dfs(int u) { vis[u] = true; tot++; for(int i=F.head[u];~i;i=F.edg[i].next){ int v = F.edg[i].to; if(!vis[v] && F.edg[i].cap>0){ dfs(v); } } } LL val[MAXN]; int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif int n,m; while(scanf("%d %d",&n,&m)==2){ F.init(); LL sum = 0; tot=0; int s = 0,t = n+1,u,v; for(int i=1;i<=n;++i){ scanf("%lld",&val[i]); if(val[i]>0){ sum += val[i]; //統計正權邊之和 F.AddEdge(s,i,val[i]); } else{ F.AddEdge(i,t,-val[i]); } } for(int i=1;i<=m;++i){ scanf("%d %d",&u,&v); F.AddEdge(u,v,INF); } LL f = F.maxFlow_sap(s,t,t+1); LL res = sum - f; //最大權值為 正權和-最小割 memset(vis,0,sizeof(vis)); dfs(s); //計算割的邊數 printf("%d %lld\n",tot-1,res); //多算了一個 } return 0; }
POJ 2987 - Firing (最大權閉合子圖)