牛客多校第一场 H-Minimum-cost Flow【最小费用最大流】

目录

  • 题意
  • 解题思路
  • 代码

参考文章: 传送门

题意

  • 链接:Minimum-cost Flow
  • 给出一个有n个点m条边的网络,边上的容量不限花费为c。有q次询问,每次询问给出两个整数u、v,问每条边的容量为u/v时,从点1发送1个单位流量到点n的最小代价为多少

解题思路

  • q可达1e5所以不能每次询问跑一次mcmf会超时
边容 运量 最小费用
原问题 u/v 1 w
扩大v/u倍 1 v/u w*u/v
扩大v倍 u v w/v
  • 根据上表我们可以先将边容扩大v/u倍,跑一次mcmf预处理出边容为1是每条增广路的费用并存起来,由于是spfa所以存时是有序的按从小到大,且由于边容都为1故每条增广路的流量都为1
    nump为总增广路的条数
    path[i]表示第i+1条增广路运送1个单位(满流量)的费用
    sum[i]表示前i条增广路运送1个单位(满流量)的费用和
  • 由于v/u不一定是整数算起来不太方便所以我们再扩大u倍,把问题转化成每条边的容量为u时,从点1发送v个单位流量到点n的最小代价为多少
  • 如果numpu 否则令v=ux+y
    运送v的最小代价ans=u*sum[x]+path[x]*y
    运送1的最小代价=ans/v

代码

#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const ll M=5005;
const ll N=5005;
const ll INF=0x7f7f7f7f;
ll n,m,s,t,mincost,sum[N];
ll cnt,head[N],cur[N],dis[N],vis[N];
struct edge{
	ll to,next,cap,cost;
}e[M<<1];
vector<ll>path;
void add(ll u,ll v,ll cap,ll cost)
{
	e[cnt].to=v;
	e[cnt].next=head[u];
	e[cnt].cap=cap;
	e[cnt].cost=cost;
	head[u]=cnt;
	cnt++;
}
bool spfa()
{
	for(ll i=0;i<=n;i++)
	{
		dis[i]=INF;vis[i]=0;
	}
	queue<ll> q;
	q.push(s);dis[s]=0;vis[s]=1;
	while(q.size())
	{
		ll u=q.front();q.pop();vis[u]=0;
		for(ll i=head[u];i!=-1;i=e[i].next)
		{
			ll v=e[i].to;
			if(e[i].cap>0&&dis[u]+e[i].cost<dis[v])
			{
				dis[v]=dis[u]+e[i].cost;
				if(!vis[v])
				{
					vis[v]=1;
					q.push(v);
				}
			}
		}
	}	
	return dis[t]!=INF;
}
ll dfs(ll u,ll flow)
{
	vis[u]=1;
	if(u==t)return flow;
	for(int i=cur[u];i!=-1;i=e[i].next)
	{
		cur[u]=i;
		ll v=e[i].to;
		if(e[i].cap>0&&dis[u]+e[i].cost==dis[v]&&!vis[v])
		{
			ll delta=dfs(v,min(e[i].cap,flow));
			if(delta>0)
			{
				e[i].cap-=delta;
				e[i^1].cap+=delta;
				mincost+=delta*e[i].cost; 
				return delta;
			}
		}
	}
	vis[u]=0;
	return 0;
}
ll getmaxflow()
{
	ll maxflow=0,delta;
	path.clear();
	while(spfa())
	{
		path.push_back(dis[t]);
		for(int i=0;i<=n;i++)cur[i]=head[i];
		while(delta=dfs(s,INF)) //找到一条增广路 
			maxflow+=delta;
	}
	return maxflow;
}
int main()
{
	ll q,u,v,f;
	while(~scanf("%d%d",&n,&m))
	{
		s=1,t=n,mincost=0,cnt=0;
		memset(head,-1,sizeof(head));
		for(int i=1;i<=m;i++)
		{
			scanf("%lld%lld%lld",&u,&v,&f);
			add(u,v,1,f);
			add(v,u,0,-f);//反边 
		}
		getmaxflow();
		ll nump=path.size();
		for(ll i=0;i<nump;i++)
			sum[i+1]=sum[i]+path[i];
		scanf("%lld",&q);
		while(q--)
		{
			scanf("%lld%lld",&u,&v);
			if(u*nump<v)printf("NaN\n");
			else{
				ll x=v/u,y=v%u;
				ll ans=sum[x]*u+y*path[x];
				ll tmp=__gcd(ans,v);
				printf("%lld/%lld\n",ans/tmp,v/tmp);
			}
		} 
	}
	return 0;
}

你可能感兴趣的:(#,7.21第一场)