【动态规划 floyd】SPOJ ACPC13
为什么rzz会把这题放在NOI模拟赛的T2?
题目大意
有一张$n$个点$m$条边的有向图,每条边有权值$w_i$。
定义一个任务$(a_i,b_i,c_i)$是如下一条路径:
- 最多经过$c_i$条边
- 路径上的边权$w_i$依次递增
要求回答$q$个独立询问的任务最小值。
$𝑛 \le 150,𝑐𝑖 \le 𝑚 \le 5000,𝑞 \le 1000,𝑤_i \le 5000$
题目分析
一道NOIP题吧
首先会想到一种dp:$f_{i,j,k}$表示对于当前处理的点$p$,路径另一端是$i$,已经过$j$条边,上一条边的权值是$k$的最小代价,时间复杂度$O(qn^2m)$。
注意到这个状态里的“上一条边权值为k”看上去很浪费,自然考虑如何去掉“w_i依次递增”的限制条件。
接下去的操作应该比较套路:$f_{i,j,k}$表示从$i$到$j$的递增路径上经过了$k$条边,然后将边从小到大加入图中,对于当前边$(u,v,w)$,就类floyd地枚举一个起始点$i$转移到$f_{i,v,k+1}$,这样就能保证路径边权是依次递增的。
时间复杂度$O(m \log m+n^2m+q)$.
1 #include<bits/stdc++.h> 2 const int maxn = 153; 3 const int maxm = 5035; 4 const int INF = 0x3f3f3f3f; 5 6 struct Edge 7 { 8 int u,v,d; 9 bool operator < (Edge a) const 10 { 11 return d < a.d; 12 } 13 }edges[maxm]; 14 int T,n,m,q,f[maxn][maxn][maxn]; 15 16 int main() 17 { 18 for (scanf("%d",&T); T; --T) 19 { 20 memset(f, 0x3f3f3f3f, sizeof f); 21 scanf("%d%d%d",&n,&m,&q); 22 for (int i=1; i<=m; i++) 23 scanf("%d%d%d",&edges[i].u,&edges[i].v,&edges[i].d); 24 std::sort(edges+1, edges+m+1); 25 for (int i=1; i<=n; i++) f[i][i][0] = 0; 26 for (int t=1; t<=m; t++) 27 { 28 int u = edges[t].u, v = edges[t].v; 29 for (int i=1; i<=n; i++) 30 for (int j=0; j<=n; j++) 31 if (f[i][u][j]!=INF&&f[i][v][j+1] > f[i][u][j]+edges[t].d) 32 f[i][v][j+1] = f[i][u][j]+edges[t].d; 33 } 34 for (int i=1; i<=n; i++) 35 for (int j=1; j<=n; j++) 36 for (int l=1; l<=n; l++) 37 f[i][j][l] = std::min(f[i][j][l], f[i][j][l-1]); 38 for (int u,v,c; q; --q) 39 { 40 scanf("%d%d%d",&u,&v,&c), c = std::min(c, n); 41 printf("%d\n",f[u][v][c]!=INF?f[u][v][c]:-1); 42 } 43 } 44 return 0; 45 }
END