【分块】【DP】HDU6331 Walking Plan

本文介绍了一种利用动态规划解决特定图上的最短路径问题的方法。具体地,对于给定的图,该算法能够高效计算出任意两点间通过特定数量边的最短路径。算法采用分块策略进行预处理,大幅降低了单次查询的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

给出n个点,m条边,询问q次,每次求从a到b经过至少k条边的最短路径。


分析:

啊啊啊啊我是傻叉吗。。。。

现场想了个什么5000+5000的分块。。。像个智障一样。。。。

明明可以100*100分块的。。。。

其实看到这应该都知道怎么做了。

定义DPa(i,j,k)DPa(i,j,k)表示从i出发,到达j点,并经过刚好k100(k100)k∗100(k≤100)条边的最短路。

定义DPb(i,j,k)DPb(i,j,k)表示从i出发,到达j点,并经过刚好k(k100)k(k≤100)条边的最短路。

定义DPc(i,j,k)DPc(i,j,k)表示从i出发,到达j点,并经过至少k(k100)k(k≤100)条边的最短路

求出两个辅助数组w(i,j)w(i,j)表示从i到j的边
dist(i,j)dist(i,j)表示从i到j的最短路

首先最好求的是DPbDPb
DPb(i,j,k)=min{DPb(i,l,k1)+w[l][j]}DPb(i,j,k)=min{DPb(i,l,k−1)+w[l][j]}

然后有了这个就很好求DPaDPaDPcDPc

DPa(i,j,k)=min{DPa(i,l,k1)+DPb(l,j,100)}DPa(i,j,k)=min{DPa(i,l,k−1)+DPb(l,j,100)}

DPc(i,j,k)=min{DPb(i,l,k)+dist(l,j)}DPc(i,j,k)=min{DPb(i,l,k)+dist(l,j)}

每次询问时枚举一个中间点ii

答案就是min{DPa(u,i,k100)+DPc(i,v,k%100)}

预处理复杂度O(100nnn)O(100∗n∗n∗n)询问复杂度O(qn)O(q∗n)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define SF scanf
#define PF printf
#define MAXN 55
#define MAXM 110
using namespace std;
int t;
int w[MAXN][MAXN];
int dp[MAXN][MAXN][MAXM],dp1[MAXN][MAXN][MAXM],dp2[MAXN][MAXN][MAXM];
int ans[MAXN];
int dist[MAXN][MAXN];
int min1(int x,int y){
    if(x==-1)
        return y;
    if(y==-1)
        return x;
    return min(x,y);
}
int main(){
    SF("%d",&t);
    int n,m,q;
    while(t--){
        SF("%d%d",&n,&m);
        memset(w,-1,sizeof w);
        int u,v,k,val;
        for(int i=1;i<=m;i++){
            SF("%d%d%d",&u,&v,&val);
            w[u][v]=min1(w[u][v],val);
        }
        memset(dist,-1,sizeof dist);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++)
                dist[i][j]=w[i][j];
            dist[i][i]=0;
        }
        memset(dp,-1,sizeof dp);
        for(int i=1;i<=n;i++)
            dp[i][i][0]=0;
        for(int k=1;k<=100;k++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)    
                    for(int l=1;l<=n;l++)
                        if(dp[i][l][k-1]!=-1&&w[l][j]!=-1)
                            dp[i][j][k]=min1(dp[i][j][k],dp[i][l][k-1]+w[l][j]);

        memset(dp1,-1,sizeof dp1);
        for(int i=1;i<=n;i++)
            dp1[i][i][0]=0;
        for(int k=1;k<=100;k++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)    
                    for(int l=1;l<=n;l++)
                        if(dp1[i][l][k-1]!=-1&&dp[l][j][100]!=-1)
                            dp1[i][j][k]=min1(dp1[i][j][k],dp1[i][l][k-1]+dp[l][j][100]);

        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                for(int k=1;k<=n;k++)
                    if(dist[j][i]!=-1&&dist[i][k]!=-1)
                        dist[j][k]=min1(dist[j][k],dist[j][i]+dist[i][k]);
        memset(dp2,-1,sizeof dp2);
        for(int k=0;k<=100;k++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    for(int l=1;l<=n;l++)
                        if(dp[i][j][k]!=-1&&dist[j][l]!=-1)
                            dp2[i][l][k]=min1(dp2[i][l][k],dp[i][j][k]+dist[j][l]);
        SF("%d",&q);
        for(int i1=1;i1<=q;i1++){
            SF("%d%d%d",&u,&v,&k);    
            int tt1=k/100;
            int tt2=k%100;
            int ans1=-1;
            for(int i=1;i<=n;i++)
                if(dp1[u][i][tt1]!=-1&&dp2[i][v][tt2]!=-1)
                    ans1=min1(ans1,dp1[u][i][tt1]+dp2[i][v][tt2]);
            PF("%d\n",ans1);
        }
    }
}
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值