题目链接: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(""); } }