2018.10.11 bzoj1877: [SDOI2009]晨跑(费用流)

传送门
日常费用流水题。


直接按照题意模拟求一下最小费用最大流就行了。
注意是点不能重合因此需要拆点。
代码:

#include<bits/stdc++.h>
#define N 505
#define M 20005
using namespace std;
struct edge{int v,next,c,w;};
int n,m;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
struct MCMF{
	edge e[M<<2];
	int first[N],cnt,s,t,d[N],pred[N],pos[N],flow[N];
	bool in[N];
	inline void init(){memset(first,-1,sizeof(first)),cnt=-1,s=1+n,t=n;}
	inline void addedge(int u,int v,int c,int w){e[++cnt].v=v,e[cnt].c=c,e[cnt].w=w,e[cnt].next=first[u],first[u]=cnt;}
	inline void add(int u,int v,int c,int w){addedge(u,v,c,w),addedge(v,u,0,-w);}
	inline bool spfa(){
		queue<int>q;
		for(int i=1;i<=n*2;++i)d[i]=0x3f3f3f3f;
		memset(in,false,sizeof(in)),d[s]=0,in[s]=1,pred[t]=-1,flow[s]=0x3f3f3f3f,q.push(s);
		while(!q.empty()){
			int x=q.front();
			q.pop(),in[x]=0;
			for(int i=first[x];~i;i=e[i].next){
				int v=e[i].v;
				if(!e[i].c||d[v]<=d[x]+e[i].w)continue;
				d[v]=d[x]+e[i].w,pred[v]=x,pos[v]=i,flow[v]=min(flow[x],e[i].c);
				if(!in[v])in[v]=1,q.push(v);
			}
		}
		return d[t]!=0x3f3f3f3f;
	}
	inline pair<int,int> solve(){
		int ret1=0,ret2=0;
		for(int w=t;spfa();w=t){
			ret1+=flow[t]*d[t],ret2+=flow[t];
			while(w!=s)e[pos[w]].c-=flow[t],e[pos[w]^1].c+=flow[t],w=pred[w];
		}
		return make_pair(ret1,ret2);
	}
}mcmf;
int main(){
	n=read(),m=read(),mcmf.init();
	for(int i=2;i<n;++i)mcmf.add(i,i+n,1,0);
	for(int i=1,u,v,w;i<=m;++i)u=read(),v=read(),w=read(),mcmf.add(u+n,v,1,w);
	pair<int,int>t=mcmf.solve();
	printf("%d %d",t.second,t.first);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值