题解
第一次遇到这种思路,觉得需要写一写
暴力的话就是直接dp
$f[x][y][j]$表示从x走到y走恰好j步最少路径长度
转移$f[x][y][i]=min(f[x][j][i-1]+edg[j][y])$
因为要至少j步,我们求出来恰好j步然后维护后缀最小值即可
期望得分$10-40$
正解是分块
同样维护$f[x][y][j]$同样维护从x走到y恰好j步最少路径长度,
多维护$f2[x][y][j]$表示走$100*j$步方案数
转移$f2[x][y][j]=min(f[x][j][100]+f2[j][y][j-1])$
然后再维护$f3$是至少$j$步(即$f$数组后缀最小值)
这样回答是$min(f2[x][j][i/100]+f3[j][y][i {Huge \%}100])$
$O(n)$回答
代码
#include<bits/stdc++.h> using namespace std; #define ll long long #define A 55 ll n,m,tot,tot2,qq,s,t,pla; ll edg[A][A],f[A][A][111],f2[A][A][111]; int main(){ // freopen("da.in","r",stdin); // freopen("ans.bf","w",stdout); scanf("%lld%lld",&n,&m); memset(edg,0x3f,sizeof(edg)); for(ll i=1;i<=m;i++){ ll a,b,c; scanf("%lld%lld%lld",&a,&b,&c); edg[a][b]=min(c,edg[a][b]); } memset(f,0x3f,sizeof(f)); memset(f2,0x3f,sizeof(f2)); for(ll k=1;k<=n;k++) f[k][k][0]=0; for(ll q=1;q<=101;q++) for(ll i=1;i<=n;i++) for(ll k=1;k<=n;k++) for(ll j=1;j<=n;j++) f[i][j][q]=min(f[i][k][q-1]+edg[k][j],f[i][j][q]); for(ll k=1;k<=n;k++) for(ll k2=1;k2<=n;k2++) f2[k][k][0]=0,f2[k][k2][1]=f[k][k2][100]; for(ll q=2;q<=101;q++) for(ll k=1;k<=n;k++) for(ll i=1;i<=n;i++) for(ll j=1;j<=n;j++){ f2[i][j][q]=min(f[i][k][100]+f2[k][j][q-1],f2[i][j][q]); // if(q<=2) // printf("f2[%lld][%lld][%lld]=%lld f[%lld][%lld][%lld]=%lld ",i,j,q,f2[i][j][q],i,j,100ll,f[i][j][100]); } for(ll q=100;q>=0;q--) for(ll i=1;i<=n;i++) for(ll j=1;j<=n;j++) f[i][j][q]=min(f[i][j][q+1],f[i][j][q]); scanf("%lld",&qq); for(ll i=1;i<=qq;i++){ scanf("%lld%lld%lld",&s,&t,&pla); ll ans=123456789101112; for(ll j=1;j<=n;j++){ ans=min(f2[s][j][pla/100]+f[j][t][pla%100],ans); // printf("f2[%lld][%lld][%lld]=%lld f[%lld][%lld][%lld]=%lld ",s,j,pla/100,f2[s][j][pla/100],j,t,pla%100,f[j][t][pla%100]); } if(ans==123456789101112) printf("-1 "); else printf("%lld ",ans); } }