BZOJ 1061 [Noi2008]志愿者招募 费用流

本文深入探讨了网络流算法在网络建模中的应用,特别是在解决特定问题时的创新建图方法。通过实例讲解,详细分析了如何利用费用流算法解决志愿者调度问题,包括边的连接、权重设定及算法实现。

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

https://www.lydsy.com/JudgeOnline/problem.php?id=1061
在这里插入图片描述
思路:网络流的建图真的很艺术……给 i i i i + 1 i+1 i+1( 1 < = i < = n 1<=i<=n 1<=i<=n)连一条权值为 i n f − a [ i ] inf-a[i] infa[i]费用为 0 0 0的边,给每个志愿者连一条从 s [ i ] s[i] s[i] t [ i ] + 1 t[i]+1 t[i]+1的权值为 i n f inf inf费用为 c [ i ] c[i] c[i]的边,然后搞一个源点和汇点跑费用流就好了。至于原因嘛,我比较菜也没什么很好的解释,大家可以感性理解一下。因为跑的是费用流,也就是最后得到的一定是最大流,所以第 i i i天和 i + 1 i+1 i+1天之间的流量一定会被带权边补上。
在这里插入图片描述

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define INF 0x3f3f3f3f
using namespace std;

const int maxn=1010;
const int maxm=12005;

struct Edge
{
	int to,nxt,f,w;
}edge[maxm<<1];

int n,m,s,t,tot;
int head[maxn]; //前向星
int dis[maxn],pre[maxn],maxf[maxn],inq[maxn];//dis用于最短路 pre记录前驱边 maxf记录最小剩余容量 inque记录是否在队列中

inline void addedge(int u,int v,int c,int w)//容量为c 权值为w
{
	edge[++tot].to=v,edge[tot].f=c,edge[tot].w=w,edge[tot].nxt=head[u],head[u]=tot;
	edge[++tot].to=u,edge[tot].f=0,edge[tot].w=-w,edge[tot].nxt=head[v],head[v]=tot;
}

bool spfa()
{
    memset(dis,INF,sizeof(dis));
    queue<int> q;
    q.push(s);
    dis[s]=0,inq[s]=1,maxf[s]=INF;
    while(!q.empty())
    {
        int u=q.front();
        q.pop(),inq[u]=0;
        for(int i=head[u];i;i=edge[i].nxt)
        {
            int v=edge[i].to;
            if(edge[i].f&&dis[v]>dis[u]+edge[i].w)
            {
                dis[v]=dis[u]+edge[i].w;
                pre[v]=i;
                maxf[v]=min(maxf[u],edge[i].f);
                if(!inq[v])
                    q.push(v),inq[v]=1;
            }
        }
    }
    return dis[t]!=INF;
}

int MCMF()
{
    int mcost=0;
    while(spfa())
    {
        int u=t,v;
        while(u!=s)
        {
            v=pre[u];
            edge[v].f-=maxf[t];
            edge[v^1].f+=maxf[t];
            u=edge[v^1].to;
        }
        mcost+=dis[t]*maxf[t];
    }
    return mcost;
}


int main()
{
	while(~scanf("%d%d%",&n,&m))
	{
		tot=1;
		memset(head,0,sizeof(head));
		int u,v,f;
		s=0,t=n+2;
		addedge(s,1,INF,0);
		addedge(n+1,t,INF,0);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&u);
			addedge(i,i+1,INF-u,0);
		}
		for(int i=0;i<m;i++)
        {
            scanf("%d%d%d",&u,&v,&f);
            addedge(u,v+1,INF,f);
        }
		printf("%d\n",MCMF());
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值