JDOJ-1908: [NOI2006]最大获利 最大权闭合子图

[文章目录]

Description

给你n个点,又给了m个关系,Ai,Bi,Ci。当你将Ai,Bi点都选了能得到Ci的收益,但是选第i个点时,会有一个花费Pi。问你能获得的最大收益。
N≤5 000,M≤50 000,0≤Ci≤100,0≤Pi≤100

网洛流。构造一个图,收益为正权点,原图点为负权点,然后,每个收益向Ai,Bi连边。然后求一下这个图的最大权闭合子图就行了。

s向所有正权点连边,边权为点的权值,负权点向汇点连边,边权为点权的绝对值,然后原图中的边保留,边权为正无穷。(别忘加反向弧,边权为0)。跑一遍最大流,再用所有正权点的点权和减去最大流就是答案了。

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
queue<int>q;
int n,m,ans,sum;
int dis[56000];
int head[56000],to[410000],nxt[410000],f[410000],cnt=1;
bool bfs()
{
    memset(dis,-1,sizeof(dis));
    while(!q.empty())q.pop();
    q.push(n+m+1);dis[n+m+1]=0;
    while(!q.empty())
    {
        int x=q.front();q.pop();
        for(int y,i=head[x];i;i=nxt[i])
            if(f[i]&&dis[y=to[i]]<0)
            {
                dis[y]=dis[x]+1;
                q.push(y);
                if(y==n+m+2) return true;
            }
    }
    return false;
}
int dinic(int x,int flow)
{
    int k,tmp=flow;
    if(x==n+m+2) return flow;
    for(int y,i=head[x];i;i=nxt[i])
        if(f[i]>0&&dis[y=to[i]]==dis[x]+1)
        {
            k=dinic(y,min(f[i],tmp));
            if(k==0) dis[y]=-1;
            tmp-=k;f[i]-=k;f[i^1]+=k;
            if(tmp==0) break;
        }
    return flow-tmp;
}
void add(int x,int y,int z)
{
    to[++cnt]=y;
    nxt[cnt]=head[x];
    f[cnt]=z;
    head[x]=cnt;
    to[++cnt]=x;
    nxt[cnt]=head[y];
    f[cnt]=0;
    head[y]=cnt;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int x,i=1;i<=n;i++)
    {
        scanf("%d",&x);
        add(i,n+m+2,x);
    }
    for(int x,y,z,i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        sum+=z;
        add(n+i,x,1<<30);
        add(n+i,y,1<<30);
        add(n+m+1,n+i,z);
    }
    while(bfs()) ans+=dinic(n+m+1,1<<30);
    printf("%d",sum-ans);
    return 0;
}

 

发表评论

邮箱地址不会被公开。 必填项已用*标注