1. 程式人生 > >CodeForces - 793D 區間dp,記憶化

CodeForces - 793D 區間dp,記憶化

記憶化 ini %d 單向 ble targe set skip ref

CodeForces - 793D

題意:一條筆直街道上有標號為 1~n 的 n 個點,有 m 條帶邊權的單向邊。要找一條經過 k 個點的路徑和,限制:每次走過的邊不能跨過已走過的點。 比如點 3 已經走過,則後面的邊不能是 min(u,v) < 3 < max(u,v) 。

tags: 80 個點,一開始上暴搜 T 了,看了題解才知道是區間 dp。。

dp[cur][l][r][skip] 為當前走了skip步在 cur點,下一步可到達區間 [l,r] 狀態,走完 k 步最少還要多少距離。

然後就是記憶化遞歸。

如果有邊 cur -> to,且 to 在[l,r]之中,則 dp[cur][l][r][skip] = min(dp[cur][l][r][skip], dp[to][st][ed][skip+1] +邊權 ) ; st, ed為走到 to後新的區間。

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define
fi first #define se second typedef long long ll; const int N = 85; int n, k, m, G[N][N]; int dp[N][N][N][N]; void Init() { mes(G, INF); mes(dp, -1); } int solve(int cur, int l, int r, int skip) { if(dp[cur][l][r][skip]!=-1) return dp[cur][l][r][skip]; // 這裏dp不要定義為INF,因為下面不可能的情況會返回INF,這樣不可能的情況就相當於沒有記憶化
if(skip==k) return dp[cur][l][r][skip] = 0; int res = INF; rep(i,l,r) if(G[cur][i]!=INF) { int st=l, ed=r; if(i<cur) ed=cur-1; else st=cur+1; res = min(res, solve(i, st, ed, skip+1)+G[cur][i] ); } return dp[cur][l][r][skip] = res; } int main() { scanf("%d%d%d", &n, &k, &m); Init(); int u, v, w; rep(i,1,m) { scanf("%d%d%d", &u, &v, &w); G[u][v] = min(G[u][v], w); } int ans=INF; rep(i,1,n) ans = min(ans, solve(i, 1, n, 1) ); if(ans==INF) ans=-1; printf("%d\n", ans); return 0; }

CodeForces - 793D 區間dp,記憶化