zoukankan      html  css  js  c++  java
  • [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 o u_1),流量为(1),费用为(0)
    (u_2 o T),流量为(1),费用为(0)
    (u_1 o v_2[u<v]),流量为1,费用为(dis_{u,v})
    (S o 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
    ",Mcmf()) ;
        return 0 ;
    }
    
  • 相关阅读:
    call方法的实现
    es6扩展字符串
    关于this的错题
    当promise实例A的resolve值为另一个promise实例对象B时,这个实例对象B的状态会替代实例对象A的状态。
    new Promise 出来的promise实例对象的默认状态是pendding,不能像then/catch方法一样返回一个新的promise实例对象!!!
    中断promise链式调用(中间返回一个pendding状态的promise)
    测试分析promise异常穿透原理
    .then内的回调函数返回结果为一个promise实例对象时,这个.then返回的promise结果就是回调函数内的promise实例对象的返回结果(等待回调函数内的promise实例对象有了结果再返回)
    vue 学习笔记(一)
    three.js初涉略(一)
  • 原文地址:https://www.cnblogs.com/beretty/p/10774653.html
Copyright © 2011-2022 走看看