題目:
官方題解:
顯然題中給圖的是一顆樹。問題可以抽象成對樹的每個點都染色,有兩中顏色可以選擇。
我們可以知道,如果某一個連通的點集染的是同一種顏色,則這個集合中只要而且必須有一個點取完整的費用,其他的點都只需要對應費用的一半。
狀態:dp[i][j][k] (0 <= i <= n, 0<=j<=1, 0 <= k <= 1) 表示以i爲根的子樹的費用,其中i節點被染成了第j種顏色,且子樹中與i染成同一種顏色的與i連通的點集有k個點選取了完整的費用。
若選取1爲根節點,則最後需要的結果爲: min(dp[1][0][1], dp[1][1][1]}。
狀態轉移方程:
v爲i節點的兒子節點。令 S = sum{min(dp[v][j][0], dp[v][1-j][1])}, det = min{dp[v][j][1] - min(dp[v][j][0], dp[v][1-j][1])};
dp[i][j][0] = cost[i][j]/2 + S;
dp[i][j][1] = min(cost[i][j] + S, cost[i][j]/2 + S + det);
個人理解:
標程:
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <vector>
#include <queue>
#include <string>
#include <set>
#include <map>
//#include <ctime>
using namespace std;
typedef long long ll;
const int N = 111;
const int INF = 0x3f3f3f3f;
int ar[2][N];
int n;
vector<int> adj[N];
int dp[N][3][3];
bool vis[N];
int dfs(int cur, int a, int b, int fa) {
//cout << cur << endl;
int &res = dp[cur][a][b];
if (res != -1) return res;
if (adj[cur].size() == 1 && adj[cur][0] == fa) {
if (b == 1) res = ar[a][cur];
else res = ar[a][cur] / 2;
} else {
for (int i = 0; i < adj[cur].size(); ++i) if (adj[cur][i] != fa) {
dfs(adj[cur][i], 0, 0, cur);
dfs(adj[cur][i], 0, 1, cur);
dfs(adj[cur][i], 1, 0, cur);
dfs(adj[cur][i], 1, 1, cur);
}
if (b == 1) {
int sum = 0;
int v, temp;
int minv = INF;
for (int i = 0; i < adj[cur].size(); ++i) {
v = adj[cur][i];
if (v != fa) {
temp = min(dp[v][a][0], dp[v][1 - a][1]);
sum += temp;
minv = min(minv, dp[v][a][1] - temp);
}
}
// if (cur == 1) cout << sum << " " << minv << endl;
res = min(ar[a][cur] + sum, ar[a][cur] / 2 + sum + minv);
} else {
res = ar[a][cur] / 2;
int v;
for (int i = 0; i < adj[cur].size(); ++i) {
v = adj[cur][i];
if (v != fa) {
res += min(dp[v][a][0], dp[v][1 - a][1]);
}
}
}
}
return res;
}
int Ans() {
memset(dp, -1, sizeof (dp));
int ans1 = dfs(1, 0, 1, -1);
int ans2 = dfs(1, 1, 1, -1);
//cout << ans1 << " " << ans2 << endl;
return min(ans1, ans2);
}
int main() {
// freopen("in", "r", stdin);
// freopen("out", "w", stdout);
while (cin >> n) {
for (int i = 1; i <= n; ++i) {
scanf("%d", &ar[0][i]);
}
for (int j = 1; j <= n; ++j) {
scanf("%d", &ar[1][j]);
}
for (int i = 1; i <= n; ++i) adj[i].clear();
int a, b;
for (int i = 1; i < n; ++i) {
scanf("%d%d", &a, &b);
adj[a].push_back(b);
adj[b].push_back(a);
}
printf("%d\n", Ans());
}
}