牛客练习赛51- F 发传单(网络流)

8 篇文章 0 订阅
3 篇文章 0 订阅

题目链接

F 发传单

题意:

一个人有 n n n 个朋友,这个人有很多传单,他可以将传单发给所有朋友,对于发给第 i i i 个人需要 w i w_i wi 的费用,他的朋友之间也有相互认识可以将传单给其他人,也需要一些费用,告诉你具体的朋友之间的认识关系和费用,要求这个人在使用最少的传单数下用最少的总费用,使得朋友都看过传单。

思路:

由于要求每一个朋友都看过,那么可以将每一个朋友进行拆点 ( i , i + n ) (i,i+n) (i,i+n) 那么这两点之间的流量一定要大于等于 1 1 1 ,将人做为源点 s s s 按照题意建边,建立一个汇点 t t t ,向所有的朋友右端点连向 t t t ,由于要求使用最小的传单数,参考一血的代码,可以将人向每一个朋友传单的费用变为 w i + i n f w_i+inf wi+inf ,最后 a n s % i n f ans\%inf ans%inf 就行了,因为是最小费用,所以可以使得所用的传单最小。现在就只要建立超级源点 S S S 和超级汇点 T T T 就可以跑有上下界的最小费用最大流了。

代码:

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int N=1e5+10;
const int M=1e6+10;
const int mx=2e6;
struct MCMF{
	int h[N],ne[M],to[M],flow[M],w[M],tot,dis[N],liu[N],pre[N],inq[N],s,t;
	int ans;
	void init(int a,int b){
		s=a;t=b;
		for(int i=0;i<M;i++)ne[i]=0;
		for(int i=0;i<N;i++)h[i]=0;
		tot=1;ans=0;
	}
	void addedge(int x,int y,int z,int c){
		to[++tot]=y;ne[tot]=h[x],h[x]=tot,flow[tot]=z;w[tot]=c;
		swap(x,y);
		to[++tot]=y;ne[tot]=h[x],h[x]=tot,flow[tot]=0;w[tot]=-c;
	}
	int bfs(){
		for(int i=0;i<=t;i++)dis[i]=inf,liu[i]=inf,inq[i]=pre[i]=0;
		queue<int>q;int x;dis[s]=0;q.push(s);
		while(!q.empty()){
			x=q.front();q.pop();inq[x]=0;
			for(int i=h[x];i;i=ne[i]){
				int v=to[i];
				if(flow[i]>0&&dis[x]+w[i]<dis[v]){
					dis[v]=dis[x]+w[i],pre[v]=i;
					liu[v]=min(liu[x],flow[i]);
					if(!inq[v])inq[v]=1,q.push(v);
				}
			}
		}
		if(!pre[t])return 0;
		x=t;ans+=dis[t]*liu[t];
		while(x!=s){
			int kl=pre[x];
			flow[kl]-=liu[t];flow[kl^1]+=liu[t];x=to[kl^1];
		}
		return 1;
	}
	int mcmf(){
		while(bfs()){}
		return ans;
	}
}F;
int d[N];
void add(int u,int v,int down,int up,int w){
	F.addedge(u,v,up-down,w);
	d[u]-=down,d[v]+=down;
}
int n,s,t,S,T;
int main()
{
	scanf("%d",&n);s=2*n+1;t=2*n+2;S=2*n+3,T=2*n+4;
	F.init(S,T);//初始化
	add(t,s,0,inf,0);//变成无汇源
	for(int i=1,x;i<=n;i++){
		scanf("%d",&x);
		add(s,i,0,inf,x+mx);
	}
	for(int i=1;i<=n;i++){
		int tt,x,y;
		add(i,i+n,1,inf,0);//下界大于一
		add(i+n,t,0,inf,0);
		scanf("%d",&tt);
		while(tt--){
			scanf("%d%d",&x,&y);
			add(i+n,x,0,inf,y);
		}
	}
	for(int i=1;i<=t;i++)if(d[i]>0)F.addedge(S,i,d[i],0);else if(d[i]<0)F.addedge(i,T,-d[i],0);
	cout<<F.mcmf()%mx<<endl;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值