题意
题解
题目的意思即求K条路覆盖所有节点,走过的总路程的最小值,有一个限制条件就是当某个人从i走到j时,途径的节点都<=j。
对于这个限制条件,我们可以简单的floyd预处理出来,得f[i][j] 为i到j的最短路,且途中的点都<=max(i,j)。
我们需要简化模型:某个人走过的一条路径肯定是分阶段的,也就是说他从某节点i经过一系列合法点到j后,需要停一停等其他人开拓了一些节点再走。我们把某一阶段的一条路径转化成一条直接的边i->j,长度为f[i][j] 。显然这样转化后往回走是没有意义的,这样就转化成了DAG图,而且限制条件已经不用考虑了。
现在问题转化为:用K条路径覆盖一个DAG中的点,路径总长度最小。这个好像是二分图匹配中的经典问题。
下面给出建图方案:
1.建立超级源S与超级汇T,将0~N拆点拆成入点i和出点i’。
2.加边 S->0, 费用0,容量K,表示人数限制
3.对于DAG中的边i->j,加边i->j’, 费用f[i][j], 容量K。(这里容量INF也一样)
4.加边 i’ -> T , 费用0,容量1。
注意到我们把从S到T单位1的流量当做一个人,他走完一个阶段还是能继续走的, 但流量已经流到汇了。为了能保证走完:
5.加边 S -> i , 费用0,容量1。
刷最小费用最大流即可。
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=355, maxe=60005;
int n,m,K,S,T,f[maxn][maxn],fir[maxn],nxt[maxe],tot=1;
int fl[maxn],dis[maxn],pth[maxn];
bool vis[maxn];
queue< int > que;
struct Edge{
int from,to,cap,flow,cost;
Edge(int x1=0,int x2=0,int x3=0,int x4=0,int x5=0):from(x1),to(x2),cap(x3),flow(x4),cost(x5){}
} Es[maxe];
void add(int x,int y,int w,int c){
Es[++tot]=Edge(x,y,w,0,c);
nxt[tot]=fir[x]; fir[x]=tot;
Es[++tot]=Edge(y,x,0,0,-c);
nxt[tot]=fir[y]; fir[y]=tot;
}
bool find_spfa(int S,int T,int &MaxFlow,int &MinCost){
memset(fl,0,sizeof(fl)); fl[S]=1e+9;
memset(dis,63,sizeof(dis)); dis[S]=0;
memset(vis,0,sizeof(vis));
while(!que.empty()) que.pop();
que.push(S); pth[S]=0;
while(!que.empty()){
int x=que.front(); que.pop();
vis[x]=false;
for(int j=fir[x];j;j=nxt[j]) if(Es[j].cap>Es[j].flow&&dis[x]+Es[j].cost<dis[Es[j].to]){
dis[Es[j].to]=dis[x]+Es[j].cost; pth[Es[j].to]=j;
fl[Es[j].to]=min(fl[x],Es[j].cap-Es[j].flow);
if(!vis[Es[j].to]) vis[Es[j].to]=true, que.push(Es[j].to);
}
}
if(!fl[T]) return false;
MaxFlow+=fl[T]; MinCost+=fl[T]*dis[T];
for(int j=pth[T];j;j=pth[Es[j].from]){
Es[j].flow+=fl[T];
Es[j^1].flow-=fl[T];
}
return true;
}
int main(){
scanf("%d%d%d",&n,&m,&K);
memset(f,63,sizeof(f));
for(int i=0;i<=n;i++) f[i][i]=0;
for(int i=1;i<=m;i++){
int x,y,z; scanf("%d%d%d",&x,&y,&z);
f[x][y]=min(f[x][y],z);
f[y][x]=f[x][y];
}
for(int k=0;k<=n;k++)
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++) if(k<=i||k<=j)
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
S=2*n+2; T=2*n+3;
add(S,0,K,0);
for(int i=1;i<=n;i++) add(S,i,1,0), add(i+n+1,T,1,0);
for(int i=0;i<=n-1;i++)
for(int j=i+1;j<=n;j++) add(i,j+n+1,1e+9,f[i][j]);
int ans1=0,ans2=0;
while(find_spfa(S,T,ans1,ans2));
printf("%d\n",ans2);
return 0;
}