luogu P4542 [ZJOI2011]营救皮卡丘

https://www.luogu.com.cn/problem/P4542

容易发现,相当于是要求m条互不相交的路径覆盖,然后发现只要路径覆盖确定了,一定就可以完成,因为可以调整相对顺序使得按照1~n的顺序覆盖

于是跑个floyd处理出 d i s [ u ] [ v ] dis[u][v] dis[u][v]表示经过编号 < = v <=v <=v的点,从 u − > v u->v u>v的最短距离

连边 ( u , v , 1 , d i s [ u ] [ v ] ) (u,v,1,dis[u][v]) (u,v,1,dis[u][v]),然后跑最小费用路径覆盖即可

code:

#include<bits/stdc++.h>
#define N 805
#define ll long long
using namespace std;
struct edge {
    int v, c, nxt;
    ll w;
} e[N * N << 1];
int p[N], eid;
void init() {
    memset(p, -1, sizeof p);
    eid = 0;
}
void insert(int u, int v, int c, ll w) {
    e[eid].v = v;
    e[eid].c = c;
    e[eid].w = w;
    e[eid].nxt = p[u];
    p[u] = eid ++;
}
void add(int u, int v, int c, ll w) { //printf("%d --> %d  %d %d\n", u, v, c, w);
    insert(u, v, c, w), insert(v, u, 0, -w);
}
int n, m, S, T, pre[N], vis[N];
ll dis[N];
queue<int> q;
const ll INF = 1e18;
const int inf = 1e9;
int bfs() {
    for(int i = 0; i <= T; i ++) dis[i] = INF, vis[i] = 0, pre[i] = -1;
    dis[S] = 0; q.push(S);
    while(q.size()) {
        int u = q.front(); q.pop();
        vis[u] = 0;
        for(int i = p[u]; i + 1; i = e[i].nxt) {
            int v = e[i].v; //printf("*%d %d %d %d  %d %d\n", u, v, dis[u], dis[v], e[i].c, e[i].w);
            if(e[i].c && dis[u] + e[i].w < dis[v]) {
                dis[v] = dis[u] + e[i].w;
                pre[v] = i;
                if(!vis[v]) vis[v] = 1, q.push(v);
            }
        }
    }
//    for(int i = 1; i <= T; i ++) printf("%lld ", dis[i]); printf("\n");
    return pre[T] != -1;
}
ll mcfc() {
    ll ret = 0;
    for(; bfs() ;) {
        ll flow = INF;
        for(int i = T; i != S; i = e[pre[i] ^ 1].v) flow = min(flow, (ll)e[pre[i]].c);
        for(int i = T; i != S; i = e[pre[i] ^ 1].v) {
            ret += flow * e[pre[i]].w;
            e[pre[i]].c -= flow, e[pre[i] ^ 1].c += flow;
        }
       // printf("*%d*", flow);
       // break;
    }
    return ret;
}
int K, diss[N][N], f[N][N];
int main() {
    init();
    scanf("%d%d%d", &n, &m, &K); n ++;
    
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= n; j ++) if(i != j) f[i][j] = inf;
    for(int i = 1; i <= m; i ++) {
        int u, v, c;
        scanf("%d%d%d", &u, &v, &c);
        u ++, v ++;
        f[u][v] = f[v][u] = min(f[u][v], c);
    }
    
    for(int k = 1; k <= n; k ++) {
        for(int i = 1; i <= n; i ++)
            for(int j = 1; j <= n; j ++) f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
        for(int i = 1; i <= n; i ++) diss[i][k] = f[i][k];
    }
    
    S = 2 * n + 1, T = S + 1;
    for(int i = 1; i <= n; i ++) {
        add(S, i, i == 1? K : 1, 0);
        add(i + n, T, 1, 0);
        for(int j = i + 1; j <= n; j ++)
            add(i, j + n, 1, diss[i][j]);
    }
    printf("%lld", mcfc());
    return 0;
}
posted @ 2021-09-28 13:38  lahlah  阅读(28)  评论(0编辑  收藏  举报