poj 2288 Islands and Bridges (狀壓dp+Tsp問題)
阿新 • • 發佈:2018-12-11
這道題千辛萬苦啊!
這道題要涉及到當前點和前面兩個點,那就設dp[state][i][j]為當前狀態為state,當前點為i,前一個點為j
這個狀態表示和之前做炮兵那題很像,就是涉及到三個點時,就多設一維表示前一個點(炮兵那題把點換成行)
這道題有很多細節需要注意
(1)計算路徑長度。這道題一開始怎麼不重複又方便的計算長度難住了我。
後來看到題解直接在初始化的時候算上路徑,非常牛逼
然後前兩個點的路徑就包含進去了。
首先加上前一個點和當前點的價值
然後看有沒有構成三角形,有就再加上
(2)更新答案。這裡更新答案要dp完了之後再弄,在dp時更新會出錯
多打幾行程式碼不會死的,重要是要ac,麻煩一點就麻煩一點
(3)初始化問題。這裡談談填表法和刷表法初始化的不同
如果是刷表法,那麼就不知道當前狀態合不合理,那就每次都需要判斷一下
一般來說一開始全部初始化為-1表示全部不合理,然後就把一開始合理的部分(比如起點)賦初值(一般為0)。
如果是填表法的話,一般來說不需要判斷合不合理
但是這道題不一樣,並不知道前兩個點的狀態是否合法,所以需要判斷。
(4)這道題有個比較坑的地方,就是n=1時要特判
(5)然後自己頭腦一定要清楚哪一個變數是第幾個點!!
我一般是寫i是當前點,j是前一個點,k是前前個點
(6)下標範圍是0到n-1,那麼就寫1 << n
(7)方案數。這道題方案數最後要除以2,因為可以反著走,但題目裡算同一種
然後dp弄方案一般可以開一個數組,意義是和dp陣列一模一樣的,只不過存的是方案數
然後符合就加上
#include<cstdio> #include<cstring> #include<algorithm> #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) using namespace std; typedef long long ll; const int MAXN = 15; int dp[(1 << 13) + 10][MAXN][MAXN], w[MAXN]; int g[MAXN][MAXN], n, m; ll ways[(1 << 13) + 10][MAXN][MAXN]; int main() { int T; scanf("%d", &T); while(T--) { memset(g, 0, sizeof(g)); memset(dp, -1, sizeof(dp)); //初始化要注意 memset(ways, 0, sizeof(ways)); scanf("%d%d", &n, &m); REP(i, 0, n) scanf("%d", &w[i]); while(m--) { int u, v; scanf("%d%d", &u, &v); u--; v--; g[u][v] = g[v][u] = 1; } if(n == 1) { printf("%d 1\n", w[0]); continue; } //特判 REP(i, 0, n) //初始化 REP(j, 0, n) if(g[i][j]) { dp[(1<<i)|(1<<j)][i][j] = w[i] + w[j] + w[i] * w[j]; ways[(1<<i)|(1<<j)][i][j] = 1; } REP(S, 0, 1 << n) REP(i, 0, n) if(S & (1 << i)) REP(j, 0, n) if((S & (1 << j)) && g[i][j]) REP(k, 0, n) if((S & (1 << k)) && g[j][k]) { if(i == j || j == k || i == k || dp[S^(1<<i)][j][k] == -1) continue; ll t = dp[S^(1<<i)][j][k] + w[i] + w[j] * w[i]; // 注意這裡哪一個是最後一點 if(g[i][k]) t += w[i] * w[j] * w[k]; if(dp[S][i][j] < t) { dp[S][i][j] = t; ways[S][i][j] = ways[S^(1<<i)][j][k]; } else if(dp[S][i][j] == t) ways[S][i][j] += ways[S^(1<<i)][j][k]; //這裡是else if 寫if會錯 } ll ans = 0, num = 0; //分開來做 int S = (1 << n) - 1; REP(i, 0, n) REP(j, 0, n) if(g[i][j]) { if(ans < dp[S][i][j]) { ans = dp[S][i][j]; num = ways[S][i][j]; } else if(ans == dp[S][i][j]) num += ways[S][i][j]; } printf("%lld %lld\n", ans, num / 2); } return 0; }