1. 程式人生 > 實用技巧 >洛谷p2515 樹形DP + Tarjan

洛谷p2515 樹形DP + Tarjan

這兩天本來應該刷刷杭電的題,週末想著放鬆一下,就研究了一波入門雜湊和tarjan。

洛谷上找了這題,tarjan套個模板寫得很快,這個再次建圖加DP花了好多時間才看懂......

杭電的題來不及刷啦嘿嘿嘿 羅總線上催補題doge

說實話這題一上來思路比較清楚,然而tarjan之後還是照搬了不少題解區內容。

主要是tarjan結束後,通過belong以及依賴關係,直接反向建圖,秀了我一臉。果然還是擺脫不了菜~

來個圖好理解.png ------------------------------------------------------------------------------------------------

題解的大部分都是參考紅色數字再建圖。 |

還看到一篇用floyd縮點的好像很有趣。那就學習一下floyd去了。hdu暫時拜拜hhh |

一想到明天週二 又要白給就很煩555 |

  1 #include<iostream>
  2 #include<algorithm>
  3 #include<cstring>
  4 #include<string>
  5 #include<vector>
  6
#include<iomanip> 7 #include<cstdio> 8 9 #define INF 1e9 10 using namespace std; 11 int n, m; 12 int t[110]; 13 int low[110]; 14 15 int cnt; 16 int now; 17 int stop;// 18 int S[110];//模擬棧 19 bool instack[110]; 20 vector<int> v[110]; 21 22 int w[110];//cost 23 int val[110];//
value 24 int rely[110];//依賴關係 25 26 vector<int> T[110];//記錄強連通分量 27 int belong[110];//判斷該點屬於哪一強連通分量 28 29 int dp[110][510];// 30 31 void initialize() 32 { 33 cnt = now = stop = 0; 34 memset(instack, false, sizeof(instack)); 35 memset(t, 0, sizeof(t)); 36 memset(belong, 0, sizeof(belong)); 37 memset(dp, 0, sizeof(dp)); 38 for(int i = 0 ; i <= 100 ; i++){ 39 v[i].clear(); 40 T[i].clear(); 41 } 42 43 } 44 45 void tarjan(int x) 46 { 47 S[++stop] = x;//入棧 48 t[x] = low[x] = ++now; 49 instack[x] = true; 50 51 for(int i = 0 ; i < v[x].size() ; i++){ 52 int nxt = v[x][i]; 53 if(t[nxt]){ 54 if(instack[nxt] && t[nxt] < t[x]){ 55 low[x] = t[nxt];// 56 } 57 }else{ 58 tarjan(nxt); 59 low[x] = min(low[x], low[nxt]); 60 } 61 } 62 if(low[x] == t[x]){ 63 cnt++; 64 int j; 65 do{ 66 j = S[stop--]; 67 instack[j] = false; 68 belong[j] = cnt; 69 }while(j != x); 70 } 71 } 72 //f[u][i]為在節點u的子樹內,費用限制為i的條件下能取到的最大價值 73 void dfs_dp(int *cost, int *value, int u)//////// 74 { 75 for(int i = cost[u] ; i <= m ; i++){ 76 dp[u][i] = value[u];//不是val[u]!! 此處下標已改為染色後 77 } 78 for(int i = 0 ; i < T[u].size() ; i++){ 79 int v = T[u][i]; 80 dfs_dp(cost, value, v); 81 for(int j = m - cost[u] ; j >= 0 ; j--){ 82 for(int q = 0 ; q <= j ; q++){ 83 dp[u][j + cost[u]] = max(dp[u][j + cost[u]], dp[u][j + cost[u] - q] + dp[v][q]); 84 //後者:其他位置節省開支,允許u的子節點v得到部分分配 85 } 86 } 87 } 88 } 89 90 void dp_solve() 91 { 92 int cost[110], value[110]; 93 int check[110]; 94 for(int i = 0 ; i <= n ; i++){ 95 cost[i] = value[i] = check[i] = 0; 96 } 97 98 for(int i = 1 ; i <= n ; i++){ 99 cost[belong[i]] += w[i]; 100 value[belong[i]] += val[i];//縮點 101 if(!rely[i] || belong[rely[i]] == belong[i]) continue; 102 T[belong[rely[i]]].push_back(belong[i]);/// 103 check[belong[i]]++; 104 } 105 ///神仙之反向建邊之花了好幾個小時才看懂系列 106 for(int i = 1 ; i <= n ; i++){ 107 if(!check[belong[i]]){ 108 T[0].push_back(belong[i]); 109 } 110 }//無頭則連至虛擬節點 111 112 dfs_dp(cost, value, 0); 113 114 printf("%d\n",dp[0][m]); 115 } 116 117 int main(){ 118 scanf("%d%d",&n,&m); 119 for(int i = 1 ; i <= n ; i++){ 120 scanf("%d",&w[i]); 121 } 122 123 for(int i = 1 ; i <= n ; i++){ 124 scanf("%d",&val[i]); 125 } 126 127 initialize(); 128 129 for(int i = 1 ; i <= n ; i++){ 130 scanf("%d",&rely[i]); 131 if(rely[i]){ 132 v[rely[i]].push_back(i);//建邊 133 } 134 } 135 136 for(int i = 1 ; i <= n ; i++){ 137 if(!t[i]){ 138 tarjan(i); 139 } 140 } 141 dp_solve(); 142 143 return 0; 144 } 145 /* 146 12 2 147 1 1 1 1 1 1 1 1 1 1 11 1 148 1 1 1 11 1 1 1 1 1 1 1 1 149 0 1 1 1 4 4 5 10 8 9 0 11 150 */