[ZJOI2011]营救皮卡丘

Posted beretty

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[ZJOI2011]营救皮卡丘相关的知识,希望对你有一定的参考价值。

技术图片


题解

似乎这玩意儿叫做\\(K\\)路径覆盖问题
可以发现\\(K\\)个人每个人走过的点集不相交
就是有\\(n\\)个点\\(m\\)条边的图,边有边权,从\\(0\\)出发,中途如果经过点\\(u\\),那么之前必须经过点\\(u-1\\),可以从点\\(S\\)出发最多\\(K\\)次,问走到\\(n\\)的最小花费
那么题目就转化成了用不超过\\(K\\)条不相交的链覆盖整张图的最小代价
可以预处理出\\(dis_{i,j}\\)表示从\\(i\\)走到\\(j\\),中途不经过\\(>j\\)的点的最短路径
然后将每个点\\(u\\)都拆成两个点\\(u_1,u_2\\)
\\(S\\to u_1\\),流量为\\(1\\),费用为\\(0\\)
\\(u_2\\to T\\),流量为\\(1\\),费用为\\(0\\)
\\(u_1\\to v_2[u<v]\\),流量为1,费用为\\(dis_{u,v}\\)
\\(S\\to 0\\),流量为\\(K\\),费用为\\(0\\)
最小费用最大流即可


代码

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int M = 405 ;
const int N = 100005 ;
const int INF = 1e9 ;
using namespace std ;

inline int read() {
    char c = getchar() ; int x = 0 , w = 1 ;
    while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
    while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
    return x*w ;
}

bool exist[M] ;
int n , m , K , S , T , ans , num = 1 ;
int hea[M] , disp[M][M] , dis[M] , pre[M] ;
struct E { int nxt , to , dis , cst ; } edge[N] ;
inline void Insert(int from , int to , int dis , int cst) {
    edge[++num].nxt = hea[from] ; edge[num].to = to ;
    edge[num].dis = dis ; edge[num].cst = cst ; hea[from] = num ;
}
inline void add_edge(int u , int v , int w , int c) {
    Insert(u , v , w , c) ;
    Insert(v , u , 0 , -c) ;
}
queue < int > q ;
inline bool Spfa() {
    q.push(S) ; pre[T] = -1 ;
    memset(dis , 63 , sizeof(dis)) ; dis[S] = 0 ;
    while(!q.empty()) {
        int u = q.front() ; q.pop() ; exist[u] = false ;
        for(int i = hea[u] ; i ; i = edge[i].nxt) {
            int v = edge[i].to ;
            if(dis[v] > dis[u] + edge[i].cst && edge[i].dis > 0) {
                dis[v] = dis[u] + edge[i].cst ; pre[v] = i ;
                if(!exist[v]) q.push(v) , exist[v] = true ;
            }
        }
    }
    return (pre[T] > 0) ;
}
inline int Mcmf() {
    while(Spfa()) {
        int diss = INF ;
        for(int i = T ; i != S ; i = edge[pre[i] ^ 1].to) diss = min(diss , edge[pre[i]].dis) ;
        for(int i = T ; i != S ; i = edge[pre[i] ^ 1].to) edge[pre[i]].dis -= diss , edge[pre[i] ^ 1].dis += diss ;
        ans += diss * dis[T] ;
    }
    return ans ;
}
int main() {
    n = read() ; m = read() ; K = read() ;
    memset(disp , 63 , sizeof(disp)) ;
    for(int i = 0 ; i <= n ; i ++) disp[i][i] = 0 ;
    for(int i = 1 , u , v , w ; i <= m ; i ++) {
        u = read() ; v = read() ; w = read() ;
        disp[u][v] = disp[v][u] = min( disp[u][v] , w ) ;
    }
    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)
                    disp[i][j] = min( disp[i][j] , disp[i][k] + disp[k][j] ) ;
    S = n * 2 + 1 ; T = n * 2 + 2 ;
    add_edge(S , 0 , K , 0) ;
    for(int i = 1 ; i <= n ; i ++) {
        add_edge(S , i , 1 , 0) ;
        add_edge(n + i , T , 1 , 0) ;
    }
    for(int u = 0 ; u < n ; u ++)
        for(int v = u + 1 ; v <= n ; v ++)
            if(disp[u][v] < INF)
                add_edge(u , v + n , 1 , disp[u][v]) ;
    printf("%d\\n",Mcmf()) ;
    return 0 ;
}

以上是关于[ZJOI2011]营救皮卡丘的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 2324 [ZJOI2011]营救皮卡丘

BZOJ2324[ZJOI2011]营救皮卡丘 有上下界费用流

BZOJ2324: [ZJOI2011]营救皮卡丘

BZOJ2324 [ZJOI2011]营救皮卡丘 费用流

[ZJOI2011]营救皮卡丘

P4542 [ZJOI2011]营救皮卡丘(Floyd+网络流)