SOJ 4299: component

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


你可能感兴趣的:(SOJ 4299: component)