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;
}