• H


    HDU - 6331

    分块DP,真是奇妙的想法。

    (dp[i][j][k])表示(i)(j)恰好(k)步的最短路。

    我们可以用(Floyd)来处理。

    然后我们再遍历整个数组,求(min),让(dp[i][j][k])表示(i)(j)至少(k)步的最短路。

    但是我们不能开(dp[55][55][10005])的数组,时间空间都会炸,怎么办呢?

    贪心地一想,如果我们的第(100)步处理好后,(dp[i][j][100])已经是最优的了,那我们(dp[i][j][200])可以直接用(dp[i][j][100])来推导出来

    定义:(dp2[i][j][k])表示(i)(j)至少(k*100)步的最短路。

    (dp/dp2)的数组更新基本相同。

    统计答案时,如果(k_i leq 100),直接输出(dp1[s_i][t_i][k_i])

    如果(100 leq k_i)(v1 = k_i / 100, v2 = k_i Mod 100),枚举中间点(p) , (Ans = min(dp[s_i][p][v2] +dp2[p][t_i][v1], dp2[s_i][p][v1] + dp[p][t_i][v2]))

    注意(k_i = 100k) 时, (v2 = 100 , v1 = 100(k-1)) ,这样比直接(Ans = dp2[s_i][t_i][k])考虑的情况更全面。

    现在想一想,为什么偏偏是(100)呢?

    因为(sqrt{max(k_i)} = sqrt{10000} = 100),这就是分块了。

    最后,贪心认为(dp[i][j][100])已经是最优的了,是为什么呢?

    理论上我们要做到(dp[i][j][10000])来证明最优,但是我们实际只要考虑到(dp[i][j][200])即可,

    反正卡着时间和空间,考虑到最大就可以了。(O(50 * 50 * k))

    
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    
    const int inf = 1e9+7;
    
    int n,m,q;
    int dp1[55][55][205],dp2[55][55][205];
    
    
    int main(){
        int T; scanf("%d",&T);
        while(T --){
            scanf("%d%d",&n,&m);
            
            for(int i = 1; i <= 50; ++ i)
            for(int j = 1; j <= 50; ++ j)
            for(int k = 1; k <= 200; ++ k)
            dp1[i][j][k] = dp2[i][j][k] = inf;
            
            for(int i = 1; i <= m; ++ i){
                int x,y,z; scanf("%d%d%d",&x,&y,&z);
                dp1[x][y][1] = min(dp1[x][y][1],z);
            }
            
            for(int p = 2; p <= 200; ++ p)
            for(int k = 1; k <= n; ++ k)
            for(int i = 1; i <= n; ++ i)
            for(int j = 1; j <= n; ++ j){
                dp1[i][j][p] = min(dp1[i][j][p], dp1[i][k][p - 1] + dp1[k][j][1]); 
            }
            
            for(int p = 199; p >= 1; -- p)
            for(int i = 1; i <= n; ++ i)
            for(int j = 1; j <= n; ++ j){
                dp1[i][j][p] = min(dp1[i][j][p], dp1[i][j][p + 1]);
            }
            
            for(int i = 1; i <= n; ++ i)
            for(int j = 1; j <= n; ++ j)
            dp2[i][j][1] = dp1[i][j][100];
            
            for(int p = 2; p <= 100; ++ p)
            for(int k = 1; k <= n; ++ k)
            for(int i = 1; i <= n; ++ i)
            for(int j = 1; j <= n; ++ j){
                dp2[i][j][p] = min(dp2[i][j][p], dp2[i][k][p - 1] + dp2[k][j][1]); 
            }
            
            scanf("%d",&q);
            while(q --){
                int s,t,k; scanf("%d%d%d",&s,&t,&k);
                int v1 = (k - 1) / 100, v2 = k - v1 * 100;
                int ans = inf;
                if(k <= 100) ans = dp1[s][t][k];
                else {
                    for(int i = 1; i <= n; ++ i)
                    ans = min(ans, dp1[s][i][v2] + dp2[i][t][v1]), ans = min(ans, dp1[s][i][v2] + dp2[i][t][v1]);
    //lalala
                }
                if(ans >= inf) printf("-1
    ");
                else printf("%d
    ",ans);
            }
        }
        return 0;
    }
    
    
  • 相关阅读:
    LeetCode 338. 比特位计数
    LeetCode 208. 实现 Trie (前缀树)
    初识restful api接口
    破解 Navicat Premium 12
    ES6 Reflect的认识
    ES6 WeakMap和WeakSet的使用场景
    sublime 注释模版插件DocBlockr的使用
    js call方法的使用
    ES6 Generator的应用场景
    ES6 Symbol的应用场景
  • 原文地址:https://www.cnblogs.com/zzhzzh123/p/13356852.html
Copyright © 2020-2023  润新知