CodeVS 2306晨跑 解题报告 最小费用最大流
题目描述 Description
Elaxia最近迷恋上了空手道,他为自己设定了一套健身计划,比如俯卧撑、仰卧起坐等等,不过到目前为止,他坚持下来的只有晨跑。
现在给出一张学校附近的地图,这张地图中包含N个十字路口和M条街道,Elaxia只能从一个十字路口跑向另外一个十字路口,街道之间只在十字路口处相交。Elaxia每天从寝室出发跑到学校,保证寝室编号为1,学校编号为N。
Elaxia的晨跑计划是按周期(包含若干天)进行的,由于他不喜欢走重复的路线,所以在一个周期内,每天的晨跑路线都不会相交(在十字路口处),寝室和学校不算十字路口。Elaxia耐力不太好,他希望在一个周期内跑的路程尽量短,但是又希望训练周期包含的天数尽量长。
除了练空手道,Elaxia其他时间都花在了学习和找MM上面,所有他想请你帮忙为他设计一套满足他要求的晨跑计划。
输入描述 Input Description
第一行:两个数N,M。表示十字路口数和街道数。
接下来M行,每行3个数a,b,c,表示路口a和路口b之间有条长度为c的街道(单向)。
输出描述 Output Description
两个数,第一个数为最长周期的天数,第二个数为满足最长天数的条件下最短的路程长度。
样例输入 Sample Input
7 10
1 2 1
1 3 1
2 4 1
3 4 1
4 5 1
4 6 1
2 5 5
3 6 6
5 7 1
6 7 1
样例输出 Sample Output
2 11
数据范围及提示 Data Size & Hint
对于30%的数据,N ≤ 20,M ≤ 120。
对于100%的数据,N ≤ 200,M ≤ 20000。
分析
拆点+最小费用最大流
把每个点都拆成两个点,i和n+i,连一条从i到n+i的边,容量为1,费用为0,对于实际图中的一条边x to y,我们在网络流图中连边x+n to y,权值与实际图中相同,容量设为1。
跑一遍最小费用最大流(我用E-K算法),第一问输出最大流flow,第二问输出最小费用cost。
简单说一下E-K算法求最小费用最大流:用SPFA在残余网络中找到一条从s到t的最短路,然后用这条路径进行增广(统计答案、加反向边),两者循环往复,直到spfa无法找到一条增广路。
一些小地方:我当时程序写错了一点小地方,陷入了永无休止的死循环,我便怀疑在残余网络中可能存在负权回路,但是不难发现,当进行某次spfa之前,所有的负权路径都是之前几次的最短路,所以当前存在的路径的长度一定大于等于这些路径,所以不会出现负权回路。
代码
//CodeVS 2306晨跑 最小费用最大流
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#define maxn 205
#define maxm 20005
using namespace std;
int d[maxn<<1], head[maxn<<1], next[maxm<<2], v[maxm<<2], w[maxm<<2],
c[maxm<<2], N, M, tot=1, pre[maxn<<1], vis[maxn<<1], ans, e[maxn<<1],
cnt;
queue<int> q;
void add(int x, int y, int z, int cc)
{v[++tot]=y;w[tot]=z;c[tot]=cc;next[tot]=head[x];head[x]=tot;}
bool spfa()
{
int p, x, t;
memset(d,0x3f,sizeof(d));
memset(vis,0,sizeof(vis));
while(!q.empty())q.pop();
d[N+1]=0;vis[N+1]=true;q.push(N+1);
while(!q.empty())
{
x=q.front();q.pop();
for(p=head[x];p;p=next[p])
{
if( c[p] && d[x]+w[p]<d[t=v[p]])
{
d[t]=d[x]+w[p];
pre[t]=x,e[t]=p;
if(!vis[t])vis[t]=true,q.push(t);
}
}
vis[x]=false;
}
return d[N]!=0x3f3f3f3f;
}
void augment()
{
int t=N, p;
while(t!=N+1)
{
p=e[t];
c[p]--;c[p^1]++;
t=pre[t];
}
ans+=d[N];
}
int main()
{
int i, j, x, y, z;
scanf("%d%d",&N,&M);
for(i=1;i<=N;i++)
add(i,N+i,0,1),add(N+i,i,0,0);
for(i=1;i<=M;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x+N,y,z,1);add(y,x+N,-z,0);
}
while(spfa())cnt++,augment();
printf("%d %d\n",cnt,ans);
return 0;
}
测试点#run0.in 结果: 内存使用量: 256kB 时间使用量: 1ms
测试点#run1.in 结果: 内存使用量: 256kB 时间使用量: 1ms
测试点#run2.in 结果: 内存使用量: 256kB 时间使用量: 1ms
测试点#run3.in 结果: 内存使用量: 256kB 时间使用量: 1ms
测试点#run4.in 结果: 内存使用量: 492kB 时间使用量: 44ms
测试点#run5.in 结果: 内存使用量: 364kB 时间使用量: 10ms
测试点#run6.in 结果: 内存使用量: 492kB 时间使用量: 45ms
测试点#run7.in 结果: 内存使用量: 744kB 时间使用量: 143ms
测试点#run8.in 结果: 内存使用量: 748kB 时间使用量: 97ms
测试点#run9.in 结果: 内存使用量: 1132kB 时间使用量: 386ms