[BZOJ1497/Luogu4174][NOI2006]最大获利

题目链接:

BZOJ1497

Luogu4174

最大权闭合子图应用。

对于每一个中转站,向汇点连边,容量为费用的绝对值。

对于每一个用户,向源点连边,容量为收益。同时向他需要的中转站连边,容量为\(\infty\)

然后所有用户的收益-上图最小割即为所求答案。

为什么呢?

因为中转站与汇点连边若割断则代表建立中转站(答案减去费用)

若割断用户与源点连边则代表放弃用户(答案减去收益)

那么上网络流即可。

这里写了\(Dinic\)(忘判细节,\(TLE*n\)加了当前弧负优化常数x4)和\(ISAP\)两种方式。

\(Dinic\quad 6320kb\quad 1012ms(BZOJ)\)

#include <queue>
#include <cstdio>
#include <cstring>

inline int Min(int a,int b){return a<b?a:b;}

int n,m,St,Ed;
int Head[55555],Next[400005],To[400005],Val[400005],En=1;
int Dep[55555],Cur[55555];

inline void Add(int x,int y,int z)
{
    Next[++En]=Head[x],To[Head[x]=En]=y,Val[En]=z;
    Next[++En]=Head[y],To[Head[y]=En]=x,Val[En]=0;
}

bool BFS()
{
    std::queue<int> q;
    memset(Dep,0,sizeof Dep);
    q.push(St),Dep[St]=1;
    for(int x,y;!q.empty();q.pop())
        for(int i=Head[x=q.front()];i;i=Next[i])
            if(Val[i]&&!Dep[y=To[i]])
            {
                Dep[y]=Dep[x]+1,q.push(y);
                if(y==Ed)return true;
            }
    return false;
}

int Dinic(int x,int Flow)
{
    if(x==Ed)return Flow;
    int Rest=Flow;
    for(int i=Head[x],y;i&&Rest;i=Next[i])
        if(Val[i]&&Dep[y=To[i]]==Dep[x]+1)
        {
            int k=Dinic(y,Min(Val[i],Rest));
            if(!k)Dep[y]=0;
            else Val[i]-=k,Val[i^1]+=k,Rest-=k;
        }
    return Flow-Rest;
}

int main()
{
    scanf("%d%d",&n,&m),St=n+m+1,Ed=St+1;
    int Ans=0;
    for(int i=1,p;i<=n;++i)
        scanf("%d",&p),Add(i,Ed,p);
    for(int i=1,a,b,c;i<=m;++i)
    {
        scanf("%d%d%d",&a,&b,&c);
        Add(i+n,a,0x3f3f3f3f);
        Add(i+n,b,0x3f3f3f3f);
        Add(St,i+n,c),Ans+=c;
    }
    int MaxFlow=0,Fs;
    while(BFS())
        while((Fs=Dinic(St,0x3f3f3f3f)))
            MaxFlow+=Fs;
    printf("%d\n",Ans-MaxFlow);
    return 0;
}

\(ISAP\quad 6596kb\quad 652ms(BZOJ)\)

#include <queue>
#include <cstdio>
#include <cstring>

inline int Min(int a,int b){return a<b?a:b;}

int n,m,St,Ed;
int Head[55555],Next[400005],To[400005],Val[400005],En=1;
int Dis[55555],Cnt[55555],Cur[55555],Pre[55555];

inline void Add(int x,int y,int z)
{
    Next[++En]=Head[x],To[Head[x]=En]=y,Val[En]=z;
    Next[++En]=Head[y],To[Head[y]=En]=x,Val[En]=0;
}

int Augment()
{
    int Res=0x3f3f3f3f;
    for(int x=Ed;x!=St;x=To[Pre[x]^1])Res=Min(Res,Val[Pre[x]]);
    for(int x=Ed,i;x!=St;x=To[i^1])Val[i=Pre[x]]-=Res,Val[i^1]+=Res;
    return Res;
}

int ISAP()
{
    Cnt[0]=Ed;
    int Flow=0,x=St;
    while(Dis[x]<Ed)
    {
        if(x==Ed)Flow+=Augment(),x=St;
        bool Flag=false;
        for(int i=Cur[x],y;i;i=Next[i])
            if(Val[i]&&Dis[x]==Dis[y=To[i]]+1)
                Flag=true,Pre[y]=Cur[x]=i,x=y,i=0;
        if(Flag)continue;
        int Wd=Ed-1;
        for(int i=Head[x];i;i=Next[i])
            if(Val[i])Wd=Min(Wd,Dis[To[i]]);
        if(!--Cnt[Dis[x]])break;
        ++Cnt[Dis[x]=Wd+1],Cur[x]=Head[x];
        if(x!=St)x=To[Pre[x]^1];
    }
    return Flow;
}

int main()
{
    scanf("%d%d",&n,&m),St=n+m+1,Ed=St+1;
    int Ans=0;
    for(int i=1,p;i<=n;++i)
        scanf("%d",&p),Add(i,Ed,p);
    for(int i=1,a,b,c;i<=m;++i)
    {
        scanf("%d%d%d",&a,&b,&c);
        Add(i+n,a,0x3f3f3f3f);
        Add(i+n,b,0x3f3f3f3f);
        Add(St,i+n,c),Ans+=c;
    }
    int MaxFlow=ISAP();
    printf("%d\n",Ans-MaxFlow);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/LanrTabe/p/10168726.html
今日推荐