题目链接:http://cstest.scu.edu.cn/soj/problem.action?id=4299
题目大意:
求树上由K个点组成的点权和最小的联通块。
算法:
树的点分治+树形结构转线性结构。
关于树的分治可以看漆子超的《分治算法在树的路径问题中的应用》。
点分治时树根不能随意指定,需找到树的重心,这样可以保证每棵子树的大小不超过N/2,从而保证递归深度不超过O(lgn)。
另外,除了分治以外,DP也不能采用普通的树上背包,要利用树的先序遍历序列进行优化,详见何森的《浅谈数据的合理组织》。
关于树型结构转线性结构的问题,UESTC论坛上何老师写的三篇文章不错:树形结构转线性结构的方法(1),(2),(3)。
代码如下:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<sstream>
#include<cstdlib>
#include<cstring>
#include<string>
#include<climits>
#include<cmath>
#include<queue>
#include<vector>
#include<stack>
#include<set>
#include<map>
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
const int MAXN=2100;
vector<int>mm[MAXN];
int w[MAXN];
int d[MAXN][MAXN];
int cot[MAXN],maxb[MAXN];
int ans[MAXN];
bool vis[MAXN];
int tot;
vector<int>node;
void dfs(int u, int p)
{
node.push_back(u);
cot[u]=1;
maxb[u]=0;
for(int i=0; i<mm[u].size(); i++)
{
int v=mm[u][i];
if(v==p||vis[v])
{
continue;
}
dfs(v,u);
cot[u]+=cot[v];
maxb[u]=max(maxb[u],cot[v]);
}
}
void solve(int u)
{
node.clear();
dfs(u,-1);
int num=node.size();
int root=-1,tmp=INT_MAX;
for(int i=0; i<node.size(); i++)
{
if(max(maxb[node[i]],num-maxb[node[i]])<tmp)
{
tmp=max(maxb[node[i]],num-maxb[node[i]]-1);
root=node[i];
}
}
node.clear();
dfs(root,-1);
for(int i=0; i<=num; i++)
{
for(int j=0; j<=num; j++)
{
d[i][j]=INF;
}
}
d[num][0]=0;
for(int i=num-1; i>=0; i--)
{
for(int j=0; j<=num; j++)
{
if(j)
{
d[i][j]=min(d[i][j],d[i+1][j-1]+w[node[i]]);
}
d[i][j]=min(d[i][j],d[i+cot[node[i]]][j]);
}
}
for(int i=1; i<=num; i++)
{
ans[i]=min(ans[i],d[1][i-1]+w[root]);
}
vis[root]=true;
for(int i=0; i<mm[root].size(); i++)
{
int v=mm[root][i];
if(!vis[v])
{
solve(v);
}
}
}
int main()
{
int n;
while(scanf("%d",&n)==1)
{
memset(vis,0,sizeof(vis));
tot=0;
for(int i=0; i<n; i++)
{
scanf("%d",&w[i]);
mm[i].clear();
}
for(int i=1; i<n; i++)
{
int u,v;
scanf("%d%d",&u,&v);
u--;
v--;
mm[u].push_back(v);
mm[v].push_back(u);
}
for(int i=1; i<=n; i++)
{
ans[i]=INT_MAX;
}
solve(0);
for(int i=1; i<=n; i++)
{
if(i>1)
{
putchar(' ');
}
printf("%d",ans[i]);
}
puts("");
}
}