/* 最大权闭合图模板类型的题,考验对知识概念的理解。 题意:现在要辞退一部分员工,辞退每一个员工可以的到一部分利益(可以是负的),并且辞退员工,必须辞退他的下属,求最大利益和辞退的最小人数。 最大权闭合图模板类型。 求出最大权后沿着源点s,dfs到的点就为最小的人数。 证明/* 转载:利用一个经典的trick:多关键字 > 建图前,对所有b[i],执行变换b[i]=b[i]*10000-1,然后,会惊异地发现, > 此时最大流所对应的方案就是满足辞退最少人数的了。 > 为什么?显然,变换后的流量r2除以10000后再取整就等于原来的流量,但是 > r2的后四位却蕴含了辞退人数的信息:每多辞退一个人,流量就会少1。 > > 剩下的就是如何根据一个网络流输出方案。 > 我的做法:从源点开始沿着残余网络dfs(只走没有满载的边), > 能dfs到的点对应的人就是需要辞退的。*/ */ #include"stdio.h" #include"string.h" #include"string" #include"queue" #define ll __int64 #define N 9999 #define inf 0x3fffffff using namespace std; struct node { ll u,v,w,next; }bian[N*50]; ll fee[N]; ll head[N],yong,s,t,dis[N]; void init(){ yong=0; memset(head,-1,sizeof(head)); memset(dis,-1,sizeof(dis)); } void addedge(ll u,ll v,ll w) { bian[yong].u=u; bian[yong].v=v; bian[yong].w=w; bian[yong].next=head[u]; head[u]=yong++; } void add(ll u,ll v,ll w) { addedge(u,v,w); addedge(v,u,0); } void bfs() { ll u,v,i; queue<ll>q; q.push(t); dis[t]=0; while(!q.empty()) { u=q.front(); q.pop(); for(i=head[u];i!=-1;i=bian[i].next) { v=bian[i].v; if(dis[v]==-1) { dis[v]=dis[u]+1; q.push(v); } } } return ; } ll ISAP() { ll sum=0; bfs(); ll gap[N],cur[N],stac[N],top,i; memset(gap,0,sizeof(gap)); for(i=s;i<=t;i++) { gap[dis[i]]++; cur[i]=head[i]; } ll k=s; top=0; while(dis[s]<t+1) { if(k==t) { ll minn=inf,index; for(i=0;i<top;i++) { ll e=stac[i]; if(minn>bian[e].w) { minn=bian[e].w; index=i; } } for(i=0;i<top;i++) { ll e=stac[i]; bian[e].w-=minn; bian[e^1].w+=minn; } sum+=minn; top=index; k=bian[stac[top]].u; } for(i=cur[k];i!=-1;i=bian[i].next) { ll v=bian[i].v; if(bian[i].w&&dis[k]==dis[v]+1) { cur[k]=i; k=v; stac[top++]=i; break; } } if(i==-1) { ll m=t+1; for(i=head[k];i!=-1;i=bian[i].next) if(m>dis[bian[i].v]&&bian[i].w) { m=dis[bian[i].v]; cur[k]=i; } if(--gap[dis[k]]==0)break; gap[dis[k]=m+1]++; if(k!=s) k=bian[stac[--top]].u; } } return sum; } ll cou=0,vis[N]; void dfs(ll u){//,ll pre) {会形成环越界 ll i,j; cou++; vis[u]=1; for(i=head[u];i!=-1;i=bian[i].next) { j=bian[i].v; if(bian[i].w&&!vis[j]) { dfs(j); } } return ; } int main() { ll n,m,i,j,sum; while(scanf("%I64d%I64d",&n,&m)!=EOF) { init(); s=0;t=n+1;sum=0; for(i=1;i<=n;i++) { scanf("%I64d",&fee[i]); if(fee[i]>0) { add(s,i,fee[i]); sum+=fee[i]; } else add(i,t,-fee[i]); } while(m--) { scanf("%I64d%I64d",&i,&j); add(i,j,inf); } cou=0; sum=sum-ISAP(); memset(vis,0,sizeof(vis)); dfs(0); printf("%I64d %I64d\n",cou-1,sum); } return 0; }
hdu 2987最大权闭合图模板类型题
时间: 2024-10-18 13:07:47