营救皮卡丘
题意
1.n个据点,m条双向路
2.K个人从0点出发
3.如果要摧毁K号据点,必须先摧毁1- (K-1)号据点
即必须按顺序摧毁据点
4.在K-1被摧毁先不能经过K号据点
5.经过K点即摧毁K点,可以反复经过
6.K个人分头行动
7.目标摧毁N号节点,并使K个人经过的道路长度和最小
解
P40
dp[i][j]:破坏了第i个据点后,每个人在j(状压位置)的经过的最少路径和
Pac
这题是最小费用流
具体流程
1.因为要摧毁所有据点,可以看成覆盖每个点
2.因为要路径和最小,可以看成费用最少
3.在保证覆盖所有点的情况下,费用最小
最大流的情况下,最小费-->最小费用流
4.首先把一个点拆成两个,一个入点,一个出点
每个点只能被进出一次
建图
1.S->0有一条0费K流的路
2.i->j有一条dis[i][j]费1流的路(每条路走一次)
dis[i][j]是不经过大于i,j的点的情况小从i->j的最短路
3.每个点都向T连一条0费1流的路
每个点出去一次
4.每个点都想S连一条0费1流的路
每个点进入一次
注意floyd求最短路时if(k>i&&k>j)continue;
不能用经过比i,j都大的点
具体代码
#include<bits/stdc++.h>
using namespace std;
const int M=505;
int n,m,K;
int dis[M][M];
int S,T;
int asdf,head[M];
struct edge {
int to,nxt,flow,cost;
} G[M*M*2];
void add_edge(int a,int b,int c,int f) {
G[++asdf].to=b;
G[asdf].nxt=head[a];
G[asdf].cost=c;
G[asdf].flow=f;
head[a]=asdf;
}
void ADD(int a,int b,int c,int f) {
add_edge(a,b,c,f);
add_edge(b,a,-c,0);
}
void floyd() {
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)continue;
if(dis[i][k]+dis[k][j]<dis[i][j]) {
dis[i][j]=dis[i][k]+dis[k][j];
}
}
}
}
}
int pre[M],last[M],Dis[M],flow[M];
bool mark[M];
bool spfa() {
queue<int>Q;
memset(mark,0,sizeof(mark));
memset(Dis,63,sizeof(Dis));
memset(flow,63,sizeof(flow));
Q.push(S);
mark[S]=1;
Dis[S]=0;
while(!Q.empty()) {
int x=Q.front();
Q.pop();
mark[x]=0;
for(int i=head[x]; i; i=G[i].nxt) {
int y=G[i].to;
if(G[i].flow>0&&Dis[y]>Dis[x]+G[i].cost) {
Dis[y]=Dis[x]+G[i].cost;
pre[y]=i;
last[y]=x;
flow[y]=min(flow[x],G[i].flow);
if(!mark[y]) {
mark[y]=1;
Q.push(y);
}
}
}
}
return Dis[T]<=1e9;
}
int solve() {
int mincost=0,maxflow=0;
while(spfa()) {
mincost+=flow[T]*Dis[T];
maxflow+=flow[T];
for(int i=T; i!=S; i=last[i]) {
G[pre[i]].flow-=flow[T];
G[pre[i]^1].flow+=flow[T];
}
}
return mincost;
}
int main() {
int a,b,c;
scanf("%d %d %d",&n,&m,&K);
memset(dis,63,sizeof(dis));
for(int i=0; i<=n; i++)dis[i][i]=0;
for(int i=1; i<=m; i++) {
scanf("%d %d %d",&a,&b,&c);
dis[a][b]=min(dis[a][b],c);
dis[b][a]=dis[a][b];
}
S=(n+1)*2,T=(n+1)*2+1;
asdf=1;
ADD(S,0,0,K);
for(int i=1; i<=n; i++) {
ADD(S,i,0,1);
ADD(i+n+1,T,0,1);
}
floyd();
for(int i=0; i<n; i++) {
for(int j=i+1; j<=n; j++) {
if(dis[i][j]<=1e9)ADD(i,j+n+1,dis[i][j],1);
}
}
printf("%d\n",solve());
return 0;
}