pku 1848 树形DP

这题就是麻烦点儿。细节比较多吧。其余没什么很天马行空的思维。

可能单纯给你这道题,让你看出是树形DP的话不太容易吧。

看上去很不好入手的一道题,但其实并不是很难做。只要想到了是树形DP。怎么说呢,这就是要学习找突破口吧。国内的赛题毕竟不如UVA或者TC之类的,学长的话说是你找不到模型的。很考想法的那种。所以说多积累一些模型还是比较有利于赛时联想然后迅速找突破口的。

对于某个节点来说,我们考虑它所在的那棵树。这个点可能和它的儿子组成一些环,也可能不跟它儿子组而余外留下它自己和上面的组环。我当时没想到还有第三种情况。其实画个图看的话,对于sample那组数据,5跟它的儿子组了一环的话,它父亲3和兄弟4就会出现了这里所说的第三种情况。那就是留下父亲x+儿子y的情况。。(这个故事告诉我们要前后考虑上下兼顾啊~!)那么可不可能留下多于一个的儿子呢?不可能。因为这两个儿子一定都要往外连去组成环,这样的话父亲x就同时出现在多于一个的环里了。所以说要么不留,要留最多留一个。(另外,我可能想不到的是儿子y还可能带着儿子的儿子z……所以说转移的时候要取dp[y][1]和dp[y][2]的较小值。)然后dp[x][0],dp[x][1],dp[x][2]分别表示这三种情况。dp[x][1],dp[x][2]很好写。但是dp[x][0]那个我发现纠结得很就没怎么想下去。其实把情况罗列清楚了仔细想想就好了。

然后是初始化。对于叶节点,我开始把三个值全部初始化成0了……显然不对。难道要初始化成INF??额……果然就是的。

大胆想,大胆做。有想法就尝试一下,这样才能提高的。

哦,本题转移dp[x][1]的时候我加了个小优化(就是那个sum),不然就是n^3了,呵呵。

 

一句话模型总结:给你一棵树,让你求个什么最值的,可以联想树形DP。不过也不局限于,因为LCA神马的也可能出来捣乱的嗯。

 

#include<iostream>
#include<cstdio>
#include<cstring>
#define INF 9999999
using namespace std;
const int maxn = 110;
const int maxm = 300;
int n;
struct node
{
    int u,v,next;
}e[maxm];
int head[maxn];
int cnt;
int dp[maxn][5];
inline void addedge(int x,int y)
{
    e[cnt].u = x;e[cnt].v = y;
    e[cnt].next = head[x];
    head[x] = cnt;
    cnt++;
    e[cnt].u = y;e[cnt].v = x;
    e[cnt].next = head[y];
    head[y] = cnt;
    cnt++;
}
void search(int xx,int f)
{
    int sum = 0;
    for(int r = head[xx];r!=-1;r = e[r].next)
    {
        int yy = e[r].v;
        if(yy!=f)
        {
            search(yy,xx);
            sum+=dp[yy][0];
        }
    }
    dp[xx][1] = INF;
    dp[xx][2] = INF;
    dp[xx][0] = INF;
    dp[xx][1] = min(sum,dp[xx][1]);
    if(sum==0)return ;
  //  dp[xx][2] = 9999999;
    for(int r = head[xx];r!=-1;r = e[r].next)
    {
        int yy = e[r].v;
        if(yy!=f)
        {
            dp[xx][2] = min(dp[xx][2],sum-dp[yy][0]+dp[yy][1]);
            dp[xx][2] = min(dp[xx][2],sum-dp[yy][0]+dp[yy][2]);
        }
    }
 //   if(dp[xx][2] == 9999999)dp[xx][2] = 0;
  //  dp[xx][0] = 9999999;
    int p,q;
    for(int r = head[xx];r!=-1;r = e[r].next)
    {
        int yy = e[r].v;
        if(yy!=f)
        {
        p = min(dp[yy][1],dp[yy][2]);
        for(int t = e[r].next;t!=-1;t = e[t].next)
        {
            int zz = e[t].v;
            if(zz!=f)
            {
            q = min(dp[zz][1],dp[zz][2]);
            dp[xx][0] = min(dp[xx][0],sum-dp[yy][0]-dp[zz][0]+p+q+1);
            }
        }
        }
    }
    for(int r = head[xx];r!=-1;r = e[r].next)
    {
        int yy = e[r].v;
        if(yy!=f)dp[xx][0] = min(dp[xx][0],sum-dp[yy][0]+dp[yy][2]+1);
    }
   // if(dp[xx][0] == 9999999)dp[xx][0] = 0;
}
int main()
{
    int i,j;
    int x,y;
    while(scanf("%d",&n)!=EOF)
    {
        memset(head,-1,sizeof(head));
        cnt = 0;
        for(i = 1;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            addedge(x,y);
        }
        int root  = 1;
        memset(dp,0,sizeof(dp));
        search(root,0);
        printf("%d/n",dp[root][0]==INF?-1:dp[root][0]);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值