no crossing(代码源每日一题)

no crossing(代码源每日一题)

no crossing - 题目 - Daimayuan Online Judge

区间DP

  1. 从暴力思路入手,站在 now 号点,当前可行的区间是 [l, r], 还要走 k 步,因此状态数为 \(n^4\), 总转移复杂度为 \(m\), 总复杂度为 \(n^4+m\)
  2. 因为路径的性质,不能横跨已经走过的点,因此 now 号点一定是下一个区间的左/右端点,因此可简化掉 now 这个状态, 总复杂度为 \(n^3+m\)

\(f[l][r][k][0/1]\) 表示当前能走的区间为 (l, r), 还有 k 步,当前站在左端点(0)还是右端点(1)

起始状态为枚举一开始站在 i 号点,i 为左端点还是右端点,即 \(dfs(0,i,k-1,1)\)\(dfs(i,n+1,k-1,0)\)

注意此处边界为 0 和 n + 1,因为边界总是之前的点走过的或者 1,n,为了方便,设 dfs 的过程中令边界都不能取,所以一开始的边界为 0,n + 1,就保证了 1,n 是可取的

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>

using namespace std;
#define endl "\n"

typedef long long ll;
typedef pair<int, int> PII;

const int INF = 0x3f3f3f3f;
const int N = 110;
int f[N][N][N][2];//f[l][r][k][0/1]为当前可行的区间是[l,r],还要走k步,当前在l/r点
struct Edge
{
	int to, val;
};
vector<vector<Edge> > G(N);
int n, k, m;
void add(int u, int v, int w)
{
	G[u].push_back({v, w});
}

int dfs(int l, int r, int k, int d)
{
	if (k <= 0)
		return 0;
	int &now = f[l][r][k][d];
	if (~now)
		return now;
	int u = (d ? r : l);
	int res = INF;
	for (auto [v, w] : G[u])
	{
		if (v <= l || v >= r)
			continue;
		res = min(res, w + dfs(v, r, k - 1, 0));
        res = min(res, w + dfs(l, v, k - 1, 1));
	}
	return now = res;
}
int main()
{
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cin >> n >> k >> m;
	while(m--)
	{
		int u, v, w;
		cin >> u >> v >> w;
		add(u, v, w);
	}
	int ans = INF;
	memset(f, -1, sizeof f);
	for (int i = 1; i <= n; i++)
	{
		ans = min(ans, dfs(i, n + 1, k - 1, 0));
        ans = min(ans, dfs(0, i, k - 1, 1));
	}
	cout << (ans == INF ? -1 : ans) << endl;
    return 0;
}
posted @ 2022-06-12 17:23  hzy0227  阅读(62)  评论(0编辑  收藏  举报