2020牛客多校训练1 H Minimum-cost Flow(最小费用流)

9 篇文章 0 订阅
3 篇文章 0 订阅

H题
在这里插入图片描述
在这里插入图片描述
先给出每条边的费用, q q q组询问,问当每条边的流量为 u / v u/v u/v时,跑到流量为1的最小费用

看到数据范围就知道肯定最多只能跑一次费用流,不然会直接 T T T

思路:求最小费用流的过程中记录每一次增广路增加的流和费用,用 m a p map map记录,最后贪心选择费用小的边进行输出(官方题解和一般的题解我一个都看不懂)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=0x3f3f3f3f,maxn=2e5+10,maxm=10005;
struct edge{
	int nex,v,cost,flow;
}e[maxm];
int head[maxn],cnt=1;
int n,m;
map<long long,long long>mp;
inline void add_edge(int u,int v,int cost,int flow){
	int ww=head[u];
	e[++cnt]={ww,v,cost,flow};
	head[u]=cnt;
}
int inque[maxn];
int dis[maxn],flow[maxn],pre[maxn],last[maxn];
bool spfa(int s,int t){
	queue<int>q;
	memset(dis,0x3f,sizeof dis);
	memset(flow,0x3f,sizeof flow);
	memset(inque,0,sizeof inque);
	q.push(s);
	inque[s]=1;
	dis[s]=0;
	pre[t]=-1;
	while(!q.empty()){
		int u=q.front();
		q.pop();
		inque[u]=0;
		for(int i=head[u];i;i=e[i].nex){
			int v=e[i].v;
			if(dis[v]>dis[u]+e[i].cost&&e[i].flow){
				dis[v]=dis[u]+e[i].cost;
				pre[v]=u;
				last[v]=i;
				flow[v]=min(flow[u],e[i].flow);
				if(!inque[v]){
					q.push(v);
					inque[v]=1;
				}
			}
		}
	}
	if(pre[t]!=-1)return true;
	return false;
}
int maxflow,mincost;
void mcmf(int s,int t){
	maxflow=0;
	mincost=0;
	while(spfa(s,t)){
		int u=t;
	//	printf("maxflow==%d %d %d\n",maxflow,flow[t],flow[t]*dis[t]);
		maxflow+=flow[t];
		mincost+=flow[t]*dis[t];
		mp[flow[t]*dis[t]]+=flow[t];
		while(u!=s){
			e[last[u]].flow-=flow[t];
			e[last[u]^1].flow+=flow[t];
			u=pre[u];
		}
	}
	return;
}
signed main(){
	while(scanf("%lld%lld",&n,&m)!=EOF){
		memset(head,0,sizeof head);
		cnt=1;
		mp.clear();
		memset(last,0,sizeof last);
		int u,v,c;
		for(int i=1;i<=m;i++){
			scanf("%lld%lld%lld",&u,&v,&c);
			add_edge(u,v,c,1);
			add_edge(v,u,-c,0);
		}
		int q;
		mcmf(1,n);
		scanf("%lld",&q);
		while(q--){
			scanf("%lld%lld",&u,&v);
			int x=0,y=v;
			if(maxflow*u<v){
		//		printf("maxflow==%d %d %d\n",maxflow,u,v);
				printf("NaN\n");
			}
			else{
			for(auto &i:mp){
//				printf("%d=======%d\n",i.first,i.second);
				if(v>i.second*u){
					v-=i.second*u;
					x+=i.first*u;
				}
				else{
					x+=i.first*v/i.second;
					break;
				}
			}
			int g=__gcd(x,y);
			printf("%lld/%lld\n",x/g,y/g);}
		}
	}
	return 0;
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值