「网络流 24 题」负载平衡

本文深入探讨了一道关于最小成本最大流算法的编程题目,通过详细的代码解析和算法思路说明,展示了如何解决这类问题。文章首先介绍了算法的基本原理,然后通过具体的代码实现,详细解释了如何将问题转化为最小成本最大流问题,并给出了完整的解决方案。

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

题目链接:https://loj.ac/problem/6013

做法:

       这道题的隐含前提是这个数肯定能被n整除,所以我们一开始就知道每个点是要流出的还是流入的,那么我们就可以根据这一点对每个点进行分类,将其分成要一定要流出的和要一定流入的,分别与源和汇相对应,即如果他要流出,那么源点连向它,流量为其多出来的值,费用为0,流入点与汇点同理。同时,每个点还要与其相邻的点相连接,这个时候就要加上费用了,因为费用是由调整造成的,再稍微注意一下环的处理就好了。网上我还看到了一些拆点的,其实不是很懂为什么要拆点,如果有大佬可以解释的话感激不尽。

 


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=400000;
const int maxm=1000000;
const int inf=0x3f3f3f3f;
int dis[maxn];
int vis[maxn],pre[maxn];
int head[maxn],cnt;
int n,m,sp,tp,a[1005];
ll ans=0;
struct node{
    int to,cap,cost,next;
}e[maxm];
void add(int from,int to,int cap,int cost){
    e[cnt].to=to; e[cnt].cap=cap;
    e[cnt].cost=cost; e[cnt].next=head[from];
    head[from]=cnt++;

    e[cnt].to=from; e[cnt].cap=0;
    e[cnt].cost=-cost; e[cnt].next=head[to];
    head[to]=cnt++;
}
bool spfa(int s,int t,int &flow,int &cost){
    queue<int> q;
    memset(dis,inf,sizeof(dis));
    memset(vis,0,sizeof(vis));
    memset(pre,-1,sizeof(pre));
    dis[s]=0;  q.push(s);
    vis[s]=1;
    int d=inf;
    while(!q.empty()){
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u];~i;i=e[i].next){
            int v=e[i].to;
            if(e[i].cap>0&&dis[v]>dis[u]+e[i].cost){
                dis[v]=dis[u]+e[i].cost;
                pre[v]=i;
                if(!vis[v]){
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
    if(dis[t]==inf){
        return false;
    }
    for(int i=pre[t];~i;i=pre[e[i^1].to]){
        d=min(d,e[i].cap);
    }
    for(int i=pre[t];~i;i=pre[e[i^1].to]){
        e[i].cap-=d;
        e[i^1].cap+=d;
        cost+=e[i].cost*d;
    }
    flow+=d;
    return true;
}
int mcmf(int s,int t){
    int flow=0,cost=0;
    while(spfa(s,t,flow,cost)){
        //cout<<flow<<" "<<cost<<endl;
    }
    return cost;
}
int main(){
    memset(head,-1,sizeof(head));
    ans=0;
    scanf("%d",&n);
    int sum=0;
    sp=0;tp=505;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        sum+=a[i];
    }
    sum/=n;
    for(int i=1;i<=n;i++){
        a[i]-=sum;
        if(a[i]>0) add(sp,i,a[i],0);
        else if(a[i]<0) add(i,tp,-a[i],0);
        add(i,i+1==n+1?1:i+1,inf,1);
        add(i,i-1==0?n:i-1,inf,1);
    }


    cout<<mcmf(sp,tp)<<endl;

    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值