P4542 [ZJOI2011]营救皮卡丘 题解

传送门:洛谷HydroOJ/bzoj

题面简述

给出一张 \(N + 1\) 个点,\(M\) 条边的无向连通图,每一条边有一个代价 \(L_i\)

\(K\) 个人,他们从 \(0\) 号结点出发,可以分开。

对于每一个结点 \(i\) 必须在已经经过 \(i - 1\) 号结点之后才能通行。

求到达 \(N\) 号结点的最小代价。

数据保证有解,且 \(N \le 150,M \le 2 \times {10}^{4},K \le 10,L_i \le {10}^{4}\)

解题思路

这一题可以转化为求选出 \(k\) 条路径,使得这 \(k\) 条路径覆盖整张图,路径之间可以有重合。

那为什么可以这么想呢。首先这 \(k\) 个人是可以分头行动的。

其次如果一个人在 \(i\) 号结点,有一条通向 \(j (j > i)\) 的边,但 \(j - 1\) 号结点还没有走过,那他是可以在原地等到 \(j - 1\) 被走过之后再走 \(j\) 号结点的。

所以无论什么时候都至少有一个人可以走动,并且在他移动后又会有人能走动,我们便将这一题转化为了上述的意思。

因为受到“对于每一个结点 \(i\) 必须在已经经过 \(i - 1\) 号结点之后才能通行。”的影响,我们需要魔改一下 floyd 算法,将 \(dis_{i,j}\) 定义为从 \(i\)\(j\) 不经过 \(> max\{i, j\}\) 的结点的最短路径,这样一来就构造了一张新图。

此时,这一题就变成了在这一张新图上求 \(k\) 条边的最小权路径覆盖。

考虑到这是一张一般图,我们将每一个结点 \(i\) 拆成 \(i_1,i_2\) 两个点,这样一来它就变成了一张二分图。

建立超级源点 \(s\) 和超级汇点 \(t\),因为这是二分图,所以连边 \((s,i_1,1,0),(i_2,t,1,0)\)

考虑到二分图的性质,我们将新图中的边 \((u,v,L_i)\) 换成 \((u_2,v_1,1,L_i)\) 或类似的连边方式。

\((s,0)\) 的这一条边的容量需要改为 \(k\) 来支持我们选择 \(k\) 条路径。

接下来就是跑最小费用最大流求解了。

时间复杂度 \(\mathcal{O}({n}^{3}+mcmf(n,{n}^{2}))\),足以通过本题。

代码

#include<iostream>
#include<queue>
#define INF ((int)1e9+(int)1e8)
using namespace std;
const int N(155),NN(2000),M(2e4+5);
int n,m,k,ds[N][N],s,t;
void floyd(){
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			for(int k=1;k<=n;k++)
				if(k<max(i,j))
					ds[j][i]=ds[i][j]=min(ds[i][k]+ds[k][j],ds[i][j]);
}
int tot=1,head[NN],to[M<<1],nxt[M<<1],cst[M<<1],flow[M<<1];
inline void add(int x,int y,int flw,int c){
	nxt[++tot]=head[x],to[head[x]=tot]=y,flow[tot]=flw,cst[tot]=c;
	nxt[++tot]=head[y],to[head[y]=tot]=x,flow[tot]=0,cst[tot]=(c==0)?0:-c;
}
bool used[NN];
int lstn[NN],lste[NN],dis[NN];
queue<int> q;
int mcmf(){
	int cost=0;
	while(114514){
		for(int i=1;i<=t;i++) dis[i]=INF;
		dis[s]=0;
		q.push(s);
		while(!q.empty()){
			int u=q.front();
			q.pop();
			used[u]=0;
			for(int i=head[u];i;i=nxt[i]){
				int v=to[i];
				if(flow[i]&&dis[v]>dis[u]+cst[i]){
					dis[v]=dis[u]+cst[i];
					lstn[v]=u;lste[v]=i;
					if(!used[v]){
						used[v]=1;
						q.push(v);
					}
				}
			}
		}
		if(dis[t]==INF) break;
		int newflow=k;
		for(int x=t;x!=s;x=lstn[x])
			newflow=min(newflow,flow[lste[x]]);
		for(int x=t;x!=s;x=lstn[x])
			flow[lste[x]]-=newflow,flow[lste[x]^1]+=newflow;
		cost+=dis[t]*newflow;
	}
	return cost;
}
int main(){
	#ifdef ytxy
	freopen("in.txt","r",stdin);
	#endif
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m>>k;
	n++;
	s=n*2+1,t=n*2+2;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			ds[i][j]=INF;
		}
		ds[i][i]=0;
	}
	for(int i=1;i<=m;i++){
		int x,y,w;
		cin>>x>>y>>w;
		ds[x+1][y+1]=ds[y+1][x+1]=min(w,ds[x+1][y+1]);
	}
	floyd();
	for(int i=1;i<=n;i++){
		if(i==1) add(s,i*2-1,k,0);
		else add(s,i*2-1,1,0);
		add(i*2,t,1,0);
		for(int j=i+1;j<=n;j++)
			add(i*2-1,j*2,1,ds[i][j]);
	}
	cout<<mcmf();
}
posted @ 2022-09-29 12:21  JR_ytxy  阅读(71)  评论(0)    收藏  举报