G. Petya and Graph(经典项目与项目消耗问题)(网络流)

题:https://codeforces.com/contest/1082/problem/G

题意:给定有边权和点权的图,问你选一些边,然sum边-sum点最大(点权被多次用为公共点只会减一次)

分析:求最大闭合子图

   选了点就要减去该点点权,选了边就加边权,然而俩点确定一边,我们可以理解为要做成一件事需要消耗(点权),事成后会有一定的利益(边权)。

   这就和网络流24题中的第二题很像了,也是经典的求利润最大化的问题,可以网络流解决,具体如下:

   总的操作:先算总的利益,再减去最小割。

   对于利益,我们连容量为利益的边到超级起点,若最大流跑过这条边,就说明这条边不取,(即原图中的边不取)即在总的操作中是减去的;

   对于消耗,我们连容量为消耗的绝对值的边到超级汇点,若最大流跑过,就说明这条边取,(即原图中的点取),在总的操作中也是呈现减去的(消耗当然对于答案来说是减去的);

   而总的利益是不变的,所以求最小的割。

G. Petya and Graph(经典项目与项目消耗问题)(网络流)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M=1e5+5;
const ll INF=1e18;
int head[M],deep[M],cur[M];
int tot,s,t;
struct node{
    int u,v,nextt;
    ll w;
}e[M<<1];
void addedge(int u,int v,ll w){
    e[tot].v=v;
    e[tot].w=w;
    e[tot].nextt=head[u];
    head[u]=tot++;
    e[tot].v=u;
    e[tot].w=0;
    e[tot].nextt=head[v];
    head[v]=tot++;
}
bool bfs(){
    for(int i=0;i<=t;i++)
        deep[i]=0;
    queue<int>que;
    que.push(s);
    deep[s]=1;
    while(!que.empty()){
        int u=que.front();
        que.pop();
        for(int i=head[u];~i;i=e[i].nextt){
            int v=e[i].v;
            if(e[i].w>0&&deep[v]==0){
                deep[v]=deep[u]+1;
                if(v==t)
                    return true;
                que.push(v);
            }
        }
    }
    return deep[t]!=0;
}
ll dfs(int u,ll fl){
    if(u==t)
        return fl;
    ll ans=0,x=0;
    for(int i=cur[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(e[i].w>0&&deep[v]==deep[u]+1){
            x=dfs(v,min(fl-ans,e[i].w));
            e[i].w-=x;
            e[i^1].w+=x;
            ans+=x;
            if(ans==fl)
                return ans;
            if(e[i].w)
                cur[u]=i;

        }
    }
    if(ans==0)
        deep[u]=0;
    return ans;
}
ll dinic(){
    ll res=0;
    while(bfs()){

        for(int i=0;i<=t;i++)
            cur[i]=head[i];
        res+=dfs(s,INF);
    }
    return res;
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    s=0,t=m+n+1;
    for(int i=0;i<=t;i++)
        head[i]=-1;
    for(int u,v,i=1;i<=n;i++){
        ll w;
        scanf("%I64d",&w);
        addedge(i,t,w);
    }

    ll ans=0;
    for(int u,v,i=1;i<=m;i++){
        ll w;
        scanf("%d%d%I64d",&u,&v,&w);
        addedge(n+i,u,INF);
        addedge(n+i,v,INF);
        addedge(s,n+i,w);
        ans+=w;
    }

    printf("%I64d\n",ans-=dinic());
    return 0;
}
View Code

 

上一篇:Petya and Array(权值线段树)


下一篇:【Codeforces 1042D】Petya and Array