【BZOJ2324】[ZJOI2011]营救皮卡丘(网络流,费用流)

【BZOJ2324】[ZJOI2011]营救皮卡丘(网络流,费用流)

题面

BZOJ
洛谷

题解

如果考虑每个人走的路径,就会很麻烦。
转过来考虑每个人破坏的点集,这样子每个人可以得到一个上升的序列。
预处理\(dis[u][v]\)表示\(u\rightarrow v\)在不经过标号大于\(max\{u,v\}\)的点的情况下的最短路。
这个可以\(Floyd\)预处理。
不妨把\(0\)号点加入到每个人的序列开头,这样子一个假设一个人的序列是\(P\),那么他经过的所有边的和就是\(\sum_{i=2}^{|P|}dis[P_{i-1}][P_i]\)
这样子就可以构建一个\(DAG\),然后要找出不超过\(k\)条路径来覆盖,对于两个点\(i,j,i<j\),从\(i\)\(j\)的费用为\(dis[i][j]\)
为了强制每个点都被访问过,把点拆成两个,因为是路径覆盖,所以每个点入度出度一进一出。
把每个点拆成入点和出点,出点作为\(i\),向其他点\(j\)的出点连边。
然后入点向\(T\)连边,表示必须有一个前驱,然后\(S\)向出点连边,表示必须有个后继。
然后\(S\)\(0\)的出点连边,容量为\(K\),表示\(K\)条路径。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define MAX 155
const int inf=150100;
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
namespace MCMF
{
	const int MAXM=1000000,MAXN=1000;
	struct Line{int v,next,w,fy;}e[MAXM];
	int h[MAXN],cnt=2;
	inline void Add(int u,int v,int w,int fy)
	{
		e[cnt]=(Line){v,h[u],w,fy};h[u]=cnt++;
		e[cnt]=(Line){u,h[v],0,-fy};h[v]=cnt++;
	}
	int dis[MAXN],pe[MAXN],pv[MAXN],Cost,Flow;
	bool vis[MAXN];queue<int> Que;
	int S=MAXN-2,T=MAXN-1;
	bool SPFA()
	{
		memset(dis,63,sizeof(dis));dis[S]=0;
		Que.push(S);vis[S]=true;
		while(!Que.empty())
		{
			int u=Que.front();Que.pop();
			for(int i=h[u];i;i=e[i].next)
			{
				int v=e[i].v;if(!e[i].w)continue;
				if(dis[u]+e[i].fy<dis[v])
				{
					dis[v]=dis[u]+e[i].fy;pe[v]=i,pv[v]=u;
					if(!vis[v])vis[v]=true,Que.push(v);
				}
			}
			vis[u]=false;
		}
		if(dis[T]>=1e9)return false;
		int flow=1e9;
		for(int i=T;i!=S;i=pv[i])flow=min(flow,e[pe[i]].w);
		for(int i=T;i!=S;i=pv[i])e[pe[i]].w-=flow,e[pe[i]^1].w+=flow;
		Flow+=flow;Cost+=dis[T]*flow;
		return true;
	}
}
using namespace MCMF;
int n,m,K,g[MAX][MAX];
int main()
{
	n=read();m=read();K=read();
	memset(g,63,sizeof(g));for(int i=0;i<=n;++i)g[i][i]=0;
	for(int i=1,u,v;i<=m;++i)u=read(),v=read(),g[u][v]=g[v][u]=min(g[u][v],read());
	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)g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
	Add(S,0+n+1,K,0);
	for(int i=1;i<=n;++i)Add(S,i+n+1,1,0),Add(i,T,1,0);
	for(int i=0;i<=n;++i)
		for(int j=i+1;j<=n;++j)
			Add(i+n+1,j,1,g[i][j]);
	while(SPFA());printf("%d\n",Cost);
	return 0;
}
posted @ 2019-03-05 17:05  小蒟蒻yyb  阅读(301)  评论(0编辑  收藏  举报