计蒜客 修建机场 解题报告

本文介绍了一个使用网络流算法解决的问题实例——如何通过构建特定的图结构来最大化机场建设项目的收益。该问题涉及到最大闭合子图的概念,并给出了详细的建图方法及C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在蒜头君生活的王国有 nnn 座城市,他们的王国计划在这些城市中修建机场,以方便国民的交通。

iii 座城市修建机场的费用为 pip_ipi。如果某两个城市 aaabbb 都修建机场,那么这两个城市的市民就可以很方便地到达对方城市,可以为国家带来的收益为 ccc。求修建机场之后的总收益减去修建机场的总费用的最大值。

输入格式

输入最多包含 202020 组测试数据,以 EOF 结束。对于每组测试数据:

第一行包含两个整数 n,mn,mn,m1≤n≤5000;1≤m≤500001 \leq n \leq 5000 ; 1 \leq m \leq 500001n5000;1m50000),其中 mmm 表示可以带来收益的城市对数。

第二行包含 nnn 个整数 pip_ipipi≤1000000p_i \leq 1000000pi1000000)。

接下来 mmm 行每行包含三个整数 a,b,ca,b,ca,b,c1≤a,b≤n;c≤10000001 \leq a , b \leq n ; c \leq 10000001a,bn;c1000000)。

输出格式

对于每组测试数据,输出一个整数。

样例输入
5 5
5 4 3 2 1
1 2 4
2 3 2
3 4 3
4 5 4
3 5 3
4 1
5 5 5 5
1 2 1
样例输出
  4
  0

网络流,最大闭合子图,讲解地址如下:http://www.hihocoder.com/contest/hiho119/problems
建图思路如下:

因为获利=收入-代价,我们需要求的就是使代价最小,我们先假设获得 了全部的收入,这样接下来就容易算代价 假设S割表示被选择了,T割表示没有被选择
做法一:
 为每条边建一个新的结点Ci(注意与收入的Ci区分),S向这个结点连容量 为Ci的边表示如果没在S割会造成Ci的代价(也就是相当于收入的逆运 算),此外Ci向这条边所连接的两个点Ai和Bi连一条容量为无穷大的边, 表示如果选了这条边则两侧的点也必须要选。 原来的点向T连一条容量为Pi的边表示如果在S割会造成Pi的代价

代码如下:
#include <bits/stdc++.h>
using namespace std;
#define maxn 100005
const int inf = 100000007;
#define ll long long
struct node{
    int to,next;
    ll flow;
};
node edge[1000000];
int head[maxn<<1],layer[maxn<<1],cnt,n,m,E;
ll ans;//注意用long long 
bool bfs(){
    queue<int>q;
    memset(layer,-1,sizeof(layer));
    layer[0]=0;
    q.push(0);
    while(!q.empty()){
        int now=q.front();
        q.pop();
        if(now==E)return true;
        for(int i=head[now];i!=-1;i=edge[i].next){
            if(edge[i].flow && layer[edge[i].to]==-1){
                layer[edge[i].to]=layer[now]+1;
                q.push(edge[i].to);
            }
        }
    }
    return false;
}
ll dfs(int pow,ll flow){
    ll sum=0;
    if(pow==E)return flow;
    for(int i=head[pow];i!=-1;i=edge[i].next){
        if(edge[i].flow && layer[edge[i].to]==layer[pow]+1){
            ll temp=dfs(edge[i].to,min(edge[i].flow,flow-sum));
            edge[i].flow-=temp;
            edge[i^1].flow+=temp;
            sum+=temp;
            if(sum==flow)return sum;
        }
    }
    if(!sum)layer[pow]=-1;
    return sum;
}
int dinic(){
    while(bfs())ans-=dfs(0,inf);
}

void add(int s,int e,ll flow){
    edge[cnt].flow=flow;
    edge[cnt].to=e;
    edge[cnt].next=head[s];
    head[s]=cnt++;

    edge[cnt].flow=0;
    edge[cnt].to=s;
    edge[cnt].next=head[e];
    head[e]=cnt++;
}
int main(){
    while(cin>>n>>m){
        E=n+m+1;
        memset(head,-1,sizeof(head));
        cnt=0;
        for(int i=1;i<=n;i++){
            int temp;
            scanf("%d",&temp);
            add(i,E,temp);
        }
        ans=0;
        for(int i=1;i<=m;i++){
            int s,e,v;
            scanf("%d%d%d",&s,&e,&v);
            ans+=v;
            add(0,i+n,v);
            add(i+n,s,inf);
            add(i+n,e,inf);
        }
        dinic();
        cout<<ans<<endl;
    }

}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值