bzoj 1497(最大权闭合图最小割)

    xiaoxiao2024-01-02  152

    传送门

    题意:

    n n n个通信塔,建立第 i i i个通讯塔需要花费 p i p_i pi元。同时有 m m m个人,对于第 i i i个人,如果 a i a_i ai号塔以及 b i b_i bi号塔被建了,则会赚 c i c_i ci元。问最大的收益。

    分析:

    最大权闭合图的经典的模型。

    所谓闭合图,即要满足一定的依赖关系,即一个事件要发生,它需要的所有前提也都一定要发生。

    而最大权闭合图即为点权最大的闭合图。

    而这类问题的一个比较经典的建图方法是:

    将能够使收益增加的点与超级源点相连,边的容量为将会增加的收益值(设之为 a i a_i ai)将能够使收益减少的点与超级汇点相连,边的容量为将会减少的收益值(设之为 b i b_i bi)将原来的闭合图之间连接一条容量为无穷的边。

    最后的闭合图的最大权为 ∑ a i − r e s \sum a_i-res aires。而 r e s res res的值为需要损失的收益且其值一定为原图的最小割。


    简单说明(全是胡扯) :

    要使得 ∑ a i − r e s \sum a_i-res aires最大,则显然我们需要损失的收益值 r e s res res最小。而因为在闭合图中,某些事件(亦或者说,某些能使得收益增加的事件)的发生,必然会导致它后面所依赖的所有事件(可能部分事件会使得收益减少)均发生,而在这个过程中,可能会产生很大的损失。

    因此我们考虑将部分边割掉。

    我们发现,如果我们将某个能使得收益增加的点与源点的边割掉,则必然会导致我们得不到该点带来的贡献,这个我们可以看作损失;同理,如果我们将某个能使得收益减少的点与汇点的边割掉,我们则需要付出一定代价,这也可以看错损失。

    而我们目前需要求得的是最小的损失 r e s res res,因此我们只需要求出原图的最小割即可。


    而对于上面的题目,我们发现,使得每个人满意是能够使得答案增加的,而使得每个人满意的制约条件是会使得答案减少的,因此我们把人和超级源点相连,灯塔与超级汇点相连,最后跑一边最大流,最后的答案即为 ∑ c i − m a x f l o w \sum c_i-maxflow cimaxflow

    代码:

    #include <bits/stdc++.h> using namespace std; const int maxn=1000005; const int inf=0x3f3f3f3f; struct Node{ int to,val,next; }q[maxn<<1]; int head[maxn],cnt=0,dep[maxn],cur[maxn],vis[maxn]; int sp,ep,maxflow; void init(){ memset(head,-1,sizeof(head)); cnt=2,maxflow=0; } void add_edge(int from,int to,int val){ q[cnt].to=to; q[cnt].val=val; q[cnt].next=head[from]; head[from]=cnt++; q[cnt].to=from; q[cnt].val=0; q[cnt].next=head[to]; head[to]=cnt++; } bool bfs(int n){ for(int i=0;i<=n;i++){ cur[i]=head[i],dep[i]=0x3f3f3f3f; vis[i]=0; } dep[sp]=0; queue<int>que; que.push(sp); while(!que.empty()){ int x=que.front(); que.pop(); vis[x]=0; for(int i=head[x];i!=-1;i=q[i].next){ int to=q[i].to; if(dep[to]>dep[x]+1&&q[i].val){ dep[to]=dep[x]+1; if(!vis[to]){ que.push(to); vis[to]=1; } } } } if(dep[ep]!=inf) return true; else return false; } int dfs(int x,int flow){ int rlow=0; if(x==ep){ maxflow+=flow; return flow; } int used=0; for(int i=cur[x];i!=-1;i=q[i].next){ cur[x]=i; int to=q[i].to; if(q[i].val&&dep[to]==dep[x]+1){ if(rlow=dfs(to,min(flow-used,q[i].val))){ used+=rlow; q[i].val-=rlow; q[i^1].val+=rlow; if(used==flow) break; } } } return used; } int dinic(int n){ while(bfs(n)){ dfs(sp,inf); } return maxflow; } int main() { int n,m; scanf("%d%d",&n,&m); init(); sp=0,ep=n+m+1; for(int i=1;i<=n;i++){ int vals; scanf("%d",&vals); add_edge(i+m,ep,vals); } int res=0; for(int i=1;i<=m;i++){ int from,to,val; scanf("%d%d%d",&from,&to,&val); add_edge(sp,i,val); add_edge(i,m+from,inf); add_edge(i,m+to,inf); res+=val; } printf("%d\n",res-dinic(n+m+1)); return 0; }
    最新回复(0)