题目:
http://poj.org/problem?id=4045
题意:
n个节点,n-1条边。求出以哪个点作为基地时,其他点到这个点的总权值最小。输出最小的总权值以及基地。
思路:
树形DP
num[u]: u点的所有子树节点个数。dp[u][0]: u点的所有子树节点到u的总距离。
dp[u][1]: 除u点的所有子树节点以外的节点到u点的总距离。
f[v][1] = (f[u][0]-f[v][0]-num[v]) + f[u][1] + n - num[v] (画个图就知道了)
AC.
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 50005;
vector g[maxn];
int n;
int num[maxn], vis[maxn];
long long dp[maxn][2];
void init()
{
for(int i = 0; i <= n; ++i) {
g[i].clear();
}
memset(num, 0, sizeof(num));
memset(vis, 0, sizeof(vis));
memset(dp, 0, sizeof(dp));
}
void dfs(int u)
{
vis[u] = 1;
num[u] = 1;
for(int i = 0; i < g[u].size(); ++i) {
int v = g[u][i];
if(vis[v]) continue;
dfs(v);
num[u] += num[v];
dp[u][0] += dp[v][0];
}
dp[u][0] += num[u]-1;
return;
}
long long ans;
void ddfs(int u)
{
vis[u] = 1;
for(int i = 0; i < g[u].size(); ++i) {
int v = g[u][i];
if(vis[v]) continue;
dp[v][1] = (dp[u][0] - dp[v][0] - num[v]) + dp[u][1] + n-num[v];
//printf("(%d, %d) %d %d %d %d\n", u, v, dp[u][0], dp[v][0], num[v], dp[u][1]);
ddfs(v);
}
ans = min(ans, dp[u][0] + dp[u][1]);
}
int main()
{
//freopen("in", "r", stdin);
int T;
scanf("%d", &T);
while(T--) {
int I, R;
scanf("%d %d %d", &n, &I, &R);
init();
for(int i = 0; i < n-1; ++i) {
int u, v;
scanf("%d %d", &u, &v);
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1);
memset(vis, 0, sizeof(vis));
ans = 1e18;
//printf("%I64d\n", ans);
ddfs(1);
bool fir = 0;
printf("%I64d\n", ans*I*I*R);
for(int i = 1; i <= n; ++i) {
if(dp[i][0]+dp[i][1] == ans) {
if(!fir) {
fir = 1;
printf("%d", i);
}
else {
printf(" %d", i);
}
}
}
printf("\n\n");
}
return 0;
}
树的重心的定义:子树节点<= n/2, 而每一棵树的重心不会超过2个。
所以求出树的重心之后以重心为根遍历整棵树求出距离。
http://acm.hust.edu.cn/vjudge/contest/viewSource.action?id=4156687