no crossing - 题目 - Daimayuan Online Judge
题意:
给出一个有向图,找一条恰好经过 k 个点的最短路径,要求每次选的边不能跃过之前已经经过的节点。即对于路径中的边 x→y ,不存在以前经过的点 tt 使得三者的编号满足 min(x,y)≤t≤max(x,y)
输入格式
第一行三个数字 n,k,m
接下来m行 , 每行 3 个整数 ai,bi,ci表示存在一条从 ai→bi , 长度为ci 的有向边。
解题思路:
题目的意思即为,在路径上,找这样的走法:
具体意思即是后来的节点的路径不能覆盖走过的点。正向的想的话,就是左右缩进的过程;逆向从小区间到大区间更新求解;
由于这样走,对之前的状态和之后的状态都不关心,符合dp的特性。那么我们可以用区间dp来解;
有向图的方向问题:0代表向右,1向左;
参考代码:
/*Keep on going Never give up*/
#include <bits/stdc++.h>
using namespace std;
#define MAX 0x7fffffff
#define pi acos(-1.0)
#define int long long
const int mod = 1e9+7;
const int N = 110;
int f[N][N][N][2];
const int inf = 1e9;
typedef pair<int, int> PII;
vector<PII> adj[N];
signed main() {
int n, m, k;
cin >> n >> k >> m;
while (m--) {
int a, b, c;
cin >> a >> b >> c;
adj[a].push_back({b, c});
}
for (int k = 1; k <= n; k++) {//已经走了K个节点;
for (int l = 0; l <= n + 1; l++) {
for (int r = l + 1; r <= n + 1; r++) {//对于每个区间:
f[k][l][r][0] = inf;//分入左端点的区间和入右端点的区间;
for (auto u : adj[l]) {//进左端点的:可到达左端点的点枚举:如果左端点可到达的节点在左区间到右区间之间,那么就是可行的。
if (u.first > l && u.first < r) {
f[k][l][r][0] = min(f[k][l][r][0], f[k - 1][l][u.first][1] + u.second);
//调个头;
f[k][l][r][0] = min(f[k][l][r][0], f[k - 1][u.first][r][0] + u.second);
//[u.first][r]左端点出来;
}
}
f[k][l][r][1] = inf;
for (auto u : adj[r]) {//从右端点进:
if (u.first > l && u.first < r) {
f[k][l][r][1] = min(f[k][l][r][1], f[k - 1][l][u.first][1] + u.second); //同上。
f[k][l][r][1] = min(f[k][l][r][1], f[k - 1][u.first][r][0] + u.second);
}
}
}
}
}
int res = inf;
for (int i = 1; i <= n; i++) {//编号1到n,按合法的路径统计:
res = min(res, f[k - 1][0][i][1]);//反向就是0向l节点;
res = min(res, f[k - 1][i][n + 1][0]);
}
if (res == inf)
cout << -1 << endl;
else
cout << res << endl;
return 0;
}