题目链接
http://www.lydsy.com/JudgeOnline/problem.php?id=2324
思路
题目有几个非常重要的地方:
1、每个点必须经过
2、经过点
i
前,
为了满足2,定义i到j的最短距离就是满足路径上每个点标号均 <=max(i,j) 的最短路径长度。
为了满足(1),我们就把每个点拆成两个点:入点和出点,每个点的入点和出点之间连容量为1,费用为-INF的边。而0点不需要拆。
S向0点连容量为
k
,费用为
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXE 201000
#define MAXV 1000
#define INF 2000000
using namespace std;
typedef long long int LL;
int S,T;
struct edge
{
int u,v,cap,w,next;
}edges[MAXE];
int head[MAXV],nCount=0;
void AddEdge(int U,int V,int C,int W)
{
edges[++nCount].u=U;
edges[nCount].v=V;
edges[nCount].cap=C;
edges[nCount].w=W;
edges[nCount].next=head[U];
head[U]=nCount;
}
void add(int U,int V,int C,int W)
{
AddEdge(U,V,C,W);
AddEdge(V,U,0,-W);
}
int q[MAXE*2],pre[MAXV],inQueue[MAXV];
LL dist[MAXV];
bool SPFA()
{
memset(pre,-1,sizeof(pre));
memset(dist,0x3f3f3f3f,sizeof(dist));
memset(inQueue,false,sizeof(inQueue));
int h=0,t=1;
q[h]=S;
dist[S]=0;
inQueue[S]=true;
while(h<t)
{
int u=q[h++];
inQueue[u]=false;
for(int p=head[u];p!=-1;p=edges[p].next)
{
int v=edges[p].v;
if(dist[u]+edges[p].w<dist[v]&&edges[p].cap) //!!!!!
{
dist[v]=dist[u]+edges[p].w;
pre[v]=p;
if(!inQueue[v])
{
inQueue[v]=true;
q[t++]=v;
}
}
}
}
return pre[T]!=-1;
}
LL MCMF()
{
LL cost=0;
while(SPFA())
{
//if(dist[T]>=INF) break;
int flow=INF;
for(int p=pre[T];p!=-1;p=pre[edges[p].u])
flow=min(flow,edges[p].cap);
for(int p=pre[T];p!=-1;p=pre[edges[p].u])
{
edges[p].cap-=flow;
edges[p^1].cap+=flow;
}
cost+=flow*dist[T];
}
return cost;
}
int dis[MAXV][MAXV];
int main()
{
int s=0; //小源点
memset(head,-1,sizeof(head));
memset(dis,0x3f3f3f3f,sizeof(dis));
nCount=1;
int n,m,K;
scanf("%d%d%d",&n,&m,&K);
for(int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
dis[u][v]=dis[v][u]=min(dis[u][v],w);
}
for(int i=1;i<=n;i++) dis[i][i]=0;
S=MAXV-2,T=MAXV-1;
for(int k=0;k<=n;k++) //dis[i][j=从i到j,只经过了标号小于等于i和j的点的最短路
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
if(k<=i||k<=j)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
add(S,s,K,0); //S向0号点连容量为K,费用为0的边,限制人数
add(s,T,K,0);
for(int i=1;i<=n;i++)
{
add(s,i,1,dis[s][i]); //S向每个点出点连容量为1,费用为0的边,补充流量
add(i,i+n,1,-INF); //设费用为-INF强制走过该边
add(i+n,T,1,0); //每个点入点向T连容量为1,费用为0,保证每个点都被访问了一次(这样做肯定满流)
}
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++) //!!!!
if(dis[i][j]<INF)
add(i+n,j,1,dis[i][j]); //对于可以到达的两个点i、j之间,连容量为INF,费用为两点间最短路的边,表示这条路径可以走无数次,并且有费用。
printf("%lld\n",MCMF()+n*INF);
return 0;
}

本文详细解析了一个经典的最小费用最大流问题实例,通过构建特殊的网络流模型来求解旅行路线问题,确保每个节点被恰好访问一次的同时,使得总费用最小。文章介绍了如何通过拆分节点、设置边的容量与费用等手段建立模型,并使用SPFA算法进行求解。
2544

被折叠的 条评论
为什么被折叠?



