zoukankan      html  css  js  c++  java
  • 【BZOJ】1497: [NOI2006]最大获利 最大权闭合子图或最小割

    【题意】给定n个点,点权为pi。m条边,边权为ci。选择一个点集的收益是在[点集中的边权和]-[点集点权和],求最大获利。n<=5000,m<=50000,0<=ci,pi<=100。

    【算法】最大权闭合子图 或 最小割

    【题解】网络流的复杂度是假的233大胆地写吧。

    把边视为连向端点的点,就是最大权闭合子图了。

    重点讲一下Amber论文中的最小割模型。

    设$d_v$表示点v的邻边边权和,$g$表示一端选一端不选的边权和(即点集和其他点的割),那么:

    $$2ans=-(-sum_v d_v+g+2*sum_v p_v)$$

    为了方便求最小割,我们在右边整体加了个负号,这样我们就是求括号内的最小值了。

    在网络流图中,对每个点x建一个点,S-x-T,割x-T表示在S割表示选,割S-x表示在T割表示不选,所以把选点的代价$2p_v-d_v$放在x-T上。

    如果点u选而点v不选,那么边(u,v)就必须加入最小割,所以从u向v连边容量为边权,这样割掉u-T和S-v后还有通路S-u-v-T。

    建图完毕后,图中有负权边。我们给所有节点加一个固定代价U(U足够大,无论选不选),这样显然不影响决策,就可以在S-x和x-T上+U,从而解决负权边的问题。

    (最小割不能直接给所有边加权,这样会破坏边权大小关系,必须要从建图方面考虑不影响决策的代价)

    最终答案就是$frac{U*n-c[S,T]}{2}$。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int n,m,S,T,p[5010];
    namespace nwf{
        const int maxn=5010,maxm=200010,inf=0x3f3f3f3f;
        int tot=1,first[maxn],d[maxn],q[maxn],cur[maxn];
        struct edge{int v,f,from;}e[maxm*2];
        void insert(int u,int v,int f){
            tot++;e[tot].v=v;e[tot].f=f;e[tot].from=first[u];first[u]=tot;
            tot++;e[tot].v=u;e[tot].f=0;e[tot].from=first[v];first[v]=tot;
        }
        bool bfs(){
            memset(d,-1,sizeof(d));
            d[S]=0;
            int head=0,tail=1;q[head]=S;
            while(head<tail){
                int x=q[head++];
                for(int i=first[x];i;i=e[i].from)if(e[i].f&&d[e[i].v]==-1){
                    d[e[i].v]=d[x]+1;
                    q[tail++]=e[i].v;
                }
            }
            return ~d[T];
        }
        int dfs(int x,int a){
            if(x==T||a==0)return a;
            int flow=0,f;
            for(int& i=cur[x];i;i=e[i].from)
            if(e[i].f&&d[e[i].v]==d[x]+1&&(f=dfs(e[i].v,min(a,e[i].f)))){
                e[i].f-=f;e[i^1].f+=f;
                flow+=f;a-=f;
                if(a==0)break;
            }
            return flow;
        }
        int dinic(){
            int ans=0;
            while(bfs()){
                for(int i=S;i<=T;i++)cur[i]=first[i];
                ans+=dfs(S,inf);
            }
            return ans;
        }
    }
    int main(){
        scanf("%d%d",&n,&m);
        S=0;T=n+1;int U;
        for(int i=1;i<=n;i++){
            int u;
            scanf("%d",&u);
            nwf::insert(i,T,u*2);
        }
        for(int i=1;i<=m;i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            nwf::insert(u,v,w);nwf::insert(v,u,w);//
            p[u]+=w;p[v]+=w;U=max(U,max(p[u],p[v]));
        }
        for(int i=1;i<=n;i++){
            nwf::insert(S,i,U);nwf::insert(i,T,U-p[i]);
        }
        printf("%lld",(1ll*U*n-nwf::dinic())/2);
        return 0;
    }
    View Code
  • 相关阅读:
    项目
    Cache Code
    是什么限制了我们面向对象
    程序设计语言本质
    不要迷失在技术的海洋中
    程序是给自己看的还是给别人看的
    程序员的春天
    新手如何学习一门新的语言
    无废话C#设计模式之二十:Mediator
    (原创)代码分析-DataGrid实现增删(带提示)改和分页
  • 原文地址:https://www.cnblogs.com/onioncyc/p/8805093.html
Copyright © 2011-2022 走看看