题意:求s到t的流量为f的最小费用流,每条边的费用是一个分段函数,当流量小于等于D[i]时,为A[i]*flow,超过时为A[i]*D[i]+B[i]*(flow-D[i]),最多只有一条边的A[i]<B[i]
一开始听叉姐提到了这个题,觉得很有趣,然后估摸着接下来也不会训这套题,于是就从官网下数据把这道题过了...
虽然说是费用流,但是其实是没有容量限制的,因此比费用流的限制要宽松很多,然后假如没有A[i],B[i]之分的话,其实就是个s到t的最短路,然后考虑有A[i]和B[i]之分,但是没有A[i]<B[i]的边,那么可以看到任意两点i->j的流量为F的流其实只会选择一条路径,假设有两条不相交路径从i到j,其中一条的流量为d,另一条流量为F-d的话,那么如果一条退1的流量,另一条加1的流量,如果费用不变,那么可以一直做到改变为止,那么由于每条路径流的越多,其单位流量费用因为A[i]>B[i]的缘故,其实是个递减的函数,因此必然存在其中一条退1的流量,另一条加1的流量会使总费用减小,然后一旦开始这个退流增流的过程,同样因为其中一个单位流量费用是减函数,另一个单位流量费用是增函数,因此会其中一个会不停退流,另一个不停地增流,直到最后两点间只有一条路径有流量,于是不考虑A[[i]<B[i]的边就可以由o(n^3)的floyd求出任意两点间流量任意的答案,因为一旦流量限定,每条边的费用就确定了。
然后考虑A[i]<B[i],他的单位流量费用是个增函数,因此前面的推理就不成立了,但是可以预见,必然至多分出一个经过A[i]<B[i]这条边的路径,假设f[p][i][j]为流量为F的从i到j的最小费用流,这个可以在o(F*n^3)的时间内算出,我们可以由f推出任意两点间经过两条路径的最小费用流,即
d[i][j]=min{f[p][i][l]+cost[l][r]+f[p][r][j]+f[F-p][i][j]}
l->r即为A[i]<B[i]的边
考虑这样转移时,两条路径可能会有重叠部分,但是不难想象,除了l->r这条边,其他边的单位流量费用是减函数,因此如果重合的话,其实是不优的,所以当我们再枚举从那两个点开始分叉和合并后,那些不合法的路径都会因为取min而被去掉。然后为了验证这个结论,我特地把枚举在那两个点分叉和合并这个过程去掉,结果发现竟然还是过了所有数据,但是我随手构个数据
3 3 0 2 3
0 1 3 2 2
1 2 3 2 1
1 2 1 1000 1
就可以把不枚举分叉点和合并点的程序给cha掉,说明日本的数据还是有点水的...
第一步预处理f的复杂度o(f*n^3),第二步求d的复杂度为o(f*n^2),最后一步求答案的复杂度为o(n^2),总复杂度o(f*n^3)
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <iostream>
const int oo=1073741819;
using namespace std;
struct point{
int x,y,A,B,D;
}G[2005];
int f[205][105][105],g[105][105][3],b[105][105],d[205][105][105];
int n,m,s,t,F;
inline void Up(int &x,int y)
{
if (x>y) x=y;
}
int main()
{
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
scanf("%d%d%d%d%d",&n,&m,&s,&t,&F);
s++,t++;
int id=0;
for (int i=1;i<=m;i++) {
scanf("%d%d%d%d%d",&G[i].x,&G[i].y,&G[i].A,&G[i].B,&G[i].D);
G[i].x++,G[i].y++;
if (G[i].A<G[i].B) id=i;
}
for (int P=1;P<=F;P++) {
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (i!=j) f[P][i][j]=d[P][i][j]=oo;else f[P][i][j]=d[P][i][j]=0;
for (int i=1;i<=m;i++) {
if (G[i].A<G[i].B) continue;
int x=G[i].x,y=G[i].y;
if (P>G[i].D) Up(f[P][x][y],G[i].A*G[i].D+G[i].B*(P-G[i].D));
else Up(f[P][x][y],P*G[i].A);
}
for (int k=1;k<=n;k++)
for (int i=1;i<=n;i++)
if (f[P][i][k]<oo)
for (int j=1;j<=n;j++)
if (f[P][k][j]<oo)
Up(f[P][i][j],f[P][i][k]+f[P][k][j]);
}
if (id)
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
for (int p=0;p<=F;p++) {
int cos=0;
if (p>G[id].D) cos=G[id].A*G[id].D+G[id].B*(p-G[id].D);
else cos=G[id].A*p;
if (f[F-p][i][j]<oo && f[p][i][G[id].x]<oo && f[p][G[id].y][j]<oo)
Up(d[F][i][j],f[F-p][i][j]+f[p][i][G[id].x]+cos+f[p][G[id].y][j]);
}
int ans=min(f[F][s][t],d[F][s][t]);
//cout<<f[F][s][s]<<endl;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (f[F][s][i]<oo && d[F][i][j]<oo && f[F][j][t]<oo)
Up(ans,f[F][s][i]+d[F][i][j]+f[F][j][t]);
if (ans<oo) printf("%d\n",ans);
else printf("Impossible\n");
return 0;
}