H - Problem M. Walking Plan

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\n");
            else printf("%d\n",ans);
        }
    }
    return 0;
}

posted @ 2020-07-21 18:56  zhuzihan  阅读(93)  评论(0编辑  收藏  举报