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();
}

浙公网安备 33010602011771号