hdu 4582 树状DP

思路:首先声明我是参考:http://blog.csdn.net/frog1902/article/details/9921845这位大牛的博客的。

他说的已经很详尽,但我还是要补充几点。

看完他的解题报告在看我的才好些。

我这的back[i]对于他的lim[i]。

我要补充的是,back[i]不是真正的back[i],可以看到这行代码:back[u]=max(back[u],dep[v]+1);也就是说是其返回祖节点再往下的一个节点。

对于没有回退边的点,其back[i]==0相当于有一个回退边到根节点,那么本节点到根节点上的任何一条边被选择都是可行的。

dp时,首先将dp[u][i](back[u]<=i<=dep[u])置为0,意义是如果在u到back[u]之间已经有一条边被其他节点选择了,那么其不考虑有子节点的话,需要的边数就是0。dp[u][dep[u]]++的意义是选择dep[u]到其父节点的边的点正是u自己。

我个人认为最难理解的是对于dp[u][j],要从dp[v][dep[v]]与0<=k<=j的深度中选取最小的dp[v][k]求和作为dp[u][j]的值。

由于dp[u][j]的意义是选择了深度为j的节点到其父节点的边后,以u为根的子树仍需要多少边,那么可以这个值也就是选择了深度为j的节点到其父节点的边后,以其子节点为根还需要dp[v][j]已经确定,那么自然以u为根的需要边数就是其子节点需要边数的和(前提是所有的选边一定要合法,合法的就是这条边一定要在back[u]到u的路径上)。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
#define Maxn 2010
using namespace std;
int dp[Maxn][Maxn],dep[Maxn],back[Maxn],n,m,vi[Maxn];
vector<int> head[Maxn];
void init()
{
    memset(dp,-1,sizeof(dp));
    memset(dep,0,sizeof(dep));
    memset(vi,0,sizeof(vi));
    memset(back,0,sizeof(back));
    for(int i=0;i<=n;i++)
        head[i].clear();
}
void add(int u,int v)
{
    head[u].push_back(v);
    head[v].push_back(u);
}
void dfs(int u)
{
    int i,v,sz;
    vi[u]=1;
    sz=head[u].size();
    for(i=0;i<sz;i++)
    {
        v=head[u][i];
        if(vi[v]) continue;
        dep[v]=dep[u]+1;
        dfs(v);
    }
}
void Treedp(int u)
{
    int i,v,sz,j;
    vi[u]=1;
    sz=head[u].size();
    for(i=back[u];i<=dep[u];i++)
        dp[u][i]=0;
    for(i=0;i<sz;i++)
    {
        v=head[u][i];
        if(vi[v]) continue;
        Treedp(v);
        int temp=dp[v][dep[v]];
        for(j=0;j<=dep[u];j++)
        {
            if(dp[v][j]!=-1)
            {
                if(temp!=-1)
                    temp=min(temp,dp[v][j]);
                else
                    temp=dp[v][j];
            }
            if(j>=back[u])
            {
                if(temp!=-1)
                    dp[u][j]+=temp;
            }
        }
    }
    if(u!=1)
    dp[u][dep[u]]++;
}
int main()
{
    int i,j,u,v;
    while(scanf("%d%d",&n,&m)!=EOF,n||m)
    {
        init();
        for(i=1;i<n;i++)
        {
            scanf("%d%d",&u,&v);
            add(u,v);
        }
        dfs(1);
        for(i=n;i<=m;i++)
        {
            scanf("%d%d",&u,&v);
            if(dep[u]<dep[v])
                swap(u,v);
            back[u]=max(back[u],dep[v]+1);
        }
        memset(vi,0,sizeof(vi));
        Treedp(1);
        printf("%d
",dp[1][0]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/wangfang20/p/3254920.html