【樹形dp小練】HDU1520 HDU2196 HDU1561 HDU3534
阿新 • • 發佈:2019-01-06
- 【樹形dp】就是在樹上做的一些
dp 之類的遞推,因為一般需要遞迴處理,因此平凡情況的處理可能需要理清思路。昨晚開始切了4 題,作為入門訓練。題目都非常簡單,但是似乎做起來都還口以~
hdu1520 Anniversary party
- 給一顆關係樹,各點有權值,選一些點出來。任一對直接相連的邊連線的點不能同時選擇,問選擇出的權值和最大為多少。
- 考慮以
u 為根的子樹可以選擇的方法,dp[u] 表示選擇u 時能獲得最大收益,dp2[u] 表示不選u 時能獲得的最大收益。則u 不選時,為dp2[u]=max{dp[v],dp2[v]}v是u的孩紙 ;u 要選時,為dp[u]=valueu+max{dp2[v]
/* **********************************************
File Name: 1520.cpp
Auther: [email protected]
Created Time: 2015年08月18日 星期二 19時50分55秒
*********************************************** */
#include <bits/stdc++.h>
using namespace std;
const int INF = 0xfffffff;
const int MAX = 6007 ;
vector<int> G[MAX];
int val[MAX];
int dp[2][MAX];
bool vis[MAX];
int n;
void dfs(int u) {
//printf("dfs %d\n", u);
vis[u] = true;
dp[0][u] = 0;
dp[1][u] = val[u];
for (auto it = G[u].begin(); it != G[u].end(); ++it) {
if (!vis[*it]) {
dfs(*it);
dp[1 ][u] += max(0, dp[0][*it]);
dp[0][u] += max(0, max(dp[0][*it], dp[1][*it]));
}
}
}
int main() {
while (cin >> n && n) {
for (int i = 1; i <= n; ++i) {
G[i].clear();
cin >> val[i];
}
int x, y;
for (int i = 1; i < n; ++i) {
cin >> x >> y;
G[x].push_back(y);
G[y].push_back(x);
}
cin >> x >> y; //0 0
//fill(dp[0], dp[0] + n + 1, -INF);
//fill(dp[1], dp[1] + n + 1, -INF);
memset(vis, false, sizeof(vis));
dfs(1); //node 1 is root
/*
for (int i = 1; i <= n; ++i) {
printf("dp[0][%d] = %d, dp[1][%d] = %d\n", i, dp[0][i], i, dp[1][i]);
}
*/
int ans = max(dp[0][1], dp[1][1]);
if (ans == 0) {
ans = -INF;
for (int i = 1; i <= n; ++i) {
if (ans < val[i]) {
ans = val[i];
}
}
}
cout << ans << endl;
}
return 0;
}
hdu2196 Computer
- 一個樹型網路,每個點代表一臺
Computer ,每個點的MaximumDistance 表示該點到樹上其它所有點的距離中最長的。求∑i=1nMDistancei - 先預處理出各點深度。然後考慮點
u 的代價,即點u 到其它某一點的最長路徑,顯然分為經過其父親節點和不經過父親節點兩種。對於前者,我們在遍歷時傳遞一個值fd 給它,表示經過父親節點到其它所有點的距離最長為多少,便可解決。對於後者,只需要考慮u 到以u 為根的子樹中某一葉子的最長距離即可,於是可以通過u 的孩子獲得。下面說明怎麼維護上述資訊。 - 對後一種情況,非常簡單地就可以做到:對每個點維護一個值,表示以該點為根的樹的高度,不妨表示為
val1u ,則val1u=max{val1v+1}v為u的孩紙 ,初始化為0. - 對前者,我們考慮
fd 首先必須包含u 的父親fa 到u 的這條邊的長度,設為len1 ,然後有兩種延伸方式,第一種是向fa 的其它若干孩紙中的一個延伸下去,第二種是向fa 的父親延伸。第二種延伸顯然已經無需額外維護,之前的遞迴已經處理完畢;第一種延伸則提示我們在搜尋fa 時,需要將fa 得到的fd′ 與fa 的其它孩紙的val1 值進行對比,取較大者作為fd 傳遞給u ,也就是用其它孩紙的val1 來動態地更新維護fd 值。這樣整個問題就做完了。 - 注意輸入
/* **********************************************
File Name: 2196.cpp
Auther: zhengdongjian@tju.edu.cn
Created Time: 2015年08月19日 星期三 08時35分06秒
*********************************************** */
#include <bits/stdc++.h>
using namespace std;
/*
* let dp[i][j]表示節點i的孩紙j所在子樹的最大深度.
* dp[i][j] = max{dp[j][k]} + 1.
* Then. dfs後繼遍歷整棵樹,父親為root,遍歷孩紙i時,
* 傳遞max{max{dp[root][k]}(k!=i), fd}和一個深度標記。
* 其中fd為root的父親傳遞進來的值。深度標記需要修正。
* 此次掃描即可得出每個點的花費
*/
typedef pair<int, int> P;
typedef pair<P, int> PP;
const int MAX = 10007;
vector<P> G[MAX];
multimap<P, int> M[MAX];
int cost[MAX];
bool vis[MAX];
int dfs(int u) {
vis[u] = true;
int res = 0;
for (auto it = G[u].begin(); it != G[u].end(); ++it) {
if (!vis[it->first]) {
int dep = dfs(it->first) + it->second;
if (dep > res) res = dep;
M[u].insert(PP(P(dep, it->first), it->second));
}
}
return res;
}
void gao(int u, int fd) {
//printf("gao %d, with %d\n", u, fd);
//cost[u] = max{fd, M[u].rbegin()->first|M[u].rbegin()-1 ->first}
cost[u] = max(fd, M[u].empty() ? 0 : M[u].rbegin()->first.first);
//printf("cost[%d] = %d\n", u, cost[u]);
if (M[u].empty()) {
return;
} else if (M[u].size() == 1) {
auto it = M[u].begin();
gao(it->first.second, it->second + fd);
} else {
for (auto it = M[u].begin(); it != M[u].end(); ++it) {
for (auto it2 = M[u].rbegin(); it2 != M[u].rend(); ++it2) {
if (it2->first.second != it->first.second) {
//printf("%d != %d\n", it2->first.second, it->first.second);
gao(it->first.second, it->second + max(fd, it2->first.first));
break;
}
}
}
}
}
int main() {
int n;
while (cin >> n) {
for (int i = 1; i <= n; ++i) {
G[i].clear();
M[i].clear();
}
int x, y;
for (int i = 2; i <= n; ++i) {
cin >> x >> y;
G[i].push_back(P(x, y));
G[x].push_back(P(i, y));
}
memset(vis, false, sizeof(vis));
dfs(1);
/*
for (int i = 1; i <= n; ++i) {
printf("%d: ", i);
for (auto it = M[i].begin(); it != M[i].end(); ++it) {
printf("<%d,%d> ", it->first.second, it->first.first);
}
puts("");
}
*/
gao(1, 0);
for (int i = 1; i <= n; ++i) {
printf("%d\n", cost[i]);
}
}
return 0;
}
hdu1561 The more, The Better
- 首先簡單分析一下。
1. 將依賴關係視為有向邊,則原關係網路為一個有向圖。2. 其次,我們發現任意一個點的入度不會大於1 ,也就是說,不會同時依賴於一個以上的點,原圖似乎是一種拓撲的存在。3. 但是要注意,可能出