费用流。
建图比较重要。
1.S->id[0][0] flow=k。 表示k条路径。
2.S->id[i][0] flow=1, 每次消耗1流量就补充1流量。
3.id[i][1]->T flow=1, 保证每个点都经过一次。
4.id[i][0]->id[j][1] 存在一条路。
这样建图每个点都会被经过一次,而且都是从编号小的点转移过来,满足题目条件。
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxn = 500 + 10; const int maxm = 100000 + 10; const int inf = 0x3f3f3f3f; int g[maxn],v[maxm],next[maxm],f[maxm],c[maxm],eid; int G[maxn][maxn]; int n,m,k,S,T; int id[maxn][2],vid; int dist[maxn],pre[maxn]; bool inque[maxn]; int q[maxm]; void addedge(int a,int b,int F,int C) { v[eid]=b; f[eid]=F; c[eid]= C; next[eid]=g[a]; g[a]=eid++; v[eid]=a; f[eid]=0; c[eid]=-C; next[eid]=g[b]; g[b]=eid++; } void build() { scanf("%d%d%d",&n,&m,&k); memset(G,0x3f,sizeof(G)); memset(g,-1,sizeof(g)); for(int i=0;i<=n;i++) G[i][i]=0; for(int i=1,a,b,l;i<=m;i++) { scanf("%d%d%d",&a,&b,&l); if(G[a][b]>l) G[a][b]=G[b][a]=l; } for(int i=0;i<=n;i++) { id[i][0]=++vid; id[i][1]=++vid; } S=++vid; T=++vid; addedge(S,id[0][0],k,0); for(int i=1;i<=n;i++) { addedge(S,id[i][0],1,0); addedge(id[i][1],T,1,0); } for(int k=0;k<=n;k++) for(int i=0;i<=n;i++) for(int j=0;j<=n;j++) { G[i][j]=min(G[i][j],G[i][k]+G[k][j]); if(k==j && i<j && G[i][j]<inf) { addedge(id[i][0],id[j][1],inf,G[i][j]); } } } bool SPFA() { int u,l,r; memset(dist,0x3f,sizeof(dist)); dist[S]=0; l=r=0; q[r++]=S; while(l<r) { inque[u=q[l++]]=0; for(int i=g[u];~i;i=next[i]) { if(f[i] && dist[v[i]]>dist[u]+c[i]) { dist[v[i]]=dist[u]+c[i]; pre[v[i]]=i; if(!inque[v[i]]) inque[q[r++]=v[i]]=1; } } } return dist[T]<inf; } int augment() { int aug=inf,res=0; for(int i=T;i!=S;i=v[pre[i]^1]) aug=min(aug,f[pre[i]]); for(int i=T;i!=S;i=v[pre[i]^1]) { f[pre[i]]-=aug; f[pre[i]^1]+=aug; res+=c[pre[i]]*aug; } return res; } void solve() { int res=0; while(SPFA()) res+=augment(); printf("%d\n",res); } int main() { build(); solve(); return 0; }