luogu P2153 [SDOI2009]晨跑

传送门

思路

观察题目,我们可以发现,这个晨跑路线就是增广路,若把每条边的容量定为一,最大流就是周期的最长天数。

基于最长天数的最短路程,对应的就是最小费用最大流。

因此根据给的数据来建边即可。

本题还有一个额外要求,就是每条路不能经过相同的点,而不同的增广路只能保证经过的边没有重复,那怎么办?

简单,只要将每一个点拆成两个点,连接拆出的两个点的边容量为一,只能过一次,自然保证了一个点不过被经过两次。

至于起点与终点,只要单独拿出来建容量为inf的边即可。

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N=2e5+10;
int n,m,s,t;

int ver[N],Next[N],head[N],val[N],c[N],tot=1;
void add(int x,int y,int z,int w)
{
	ver[++tot]=y;val[tot]=z;c[tot]=w;Next[tot]=head[x];head[x]=tot;
	ver[++tot]=x;val[tot]=0;c[tot]=-w;Next[tot]=head[y];head[y]=tot;
}

int dis[N],vis[N],pre[N],incf[N];
bool bfs()
{
	memset(vis,0,sizeof(vis));
	memset(dis,0x3f,sizeof(dis));
	queue<int>q;
	q.push(s);incf[s]=1e9;dis[s]=0;
	while(!q.empty())
	{
		int u=q.front();q.pop();vis[u]=0;
		for(int i=head[u];i;i=Next[i])
		{
			int v=ver[i];
			if(dis[v]>dis[u]+c[i]&&val[i])
			{
				dis[v]=dis[u]+c[i];
				incf[v]=min(incf[u],val[i]);
				pre[v]=i;
				if(!vis[v]){
					vis[v]=1;q.push(v);
				}
			}
		}
	}
	if(dis[t]>1e9)return 0;
	return 1;
}

int maxn,minn;
void update()
{
	int u=t;
	while(u!=s)
	{
		int i=pre[u];
		val[i]-=incf[t];
		val[i^1]+=incf[t];
		u=ver[i^1];
	}
	maxn+=incf[t];
	minn+=incf[t]*dis[t];
}

int main()
{
	scanf("%d%d",&n,&m);
	s=1;t=n*2;
	add(1,1+n,1e9,0);
	add(n,n*2,1e9,0);
	for(int i=2;i<=n-1;i++)add(i,i+n,1,0);
	while(m--)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		add(x+n,y,1,z);
	}
	while(bfs())update();
	printf("%d %d",maxn,minn);
	return 0;
}
原文地址:https://www.cnblogs.com/luotao0034/p/14441869.html