ZJK的黑OJ(树的最大独立集)(树形DP)

动态规划零基础 同时被 2 个专栏收录
66 篇文章 0 订阅
49 篇文章 0 订阅

ZJK的黑OJ
zjk开了一家“善良OJ”。这其实是家黑OJ。每AC一道题,网站便会自动在电脑上安装一种木马。zjk通过窃取信息获取收益(如网游帐号、OI资料、和KK的照片等等)。
作为一名资深黑客,老Z某日突然发现,“善良OJ”上的木马,自己电脑上都没有。这可十分让他过意不去。老Z决定通过多A题,来丰富自己电脑的病毒库。
经过调查,老Z发现,很多木马是不能共存的。比如“和谐”木马与“团结”木马,两者只能任选其一。然而,老Z是个完美主义者,他想要自己的病毒库尽可能充实。
老Z不懈的追求最终感动了上天。天上的神仙lemon给这个问题稍稍降低了一点难度。神仙规定,对于n种木马,有且仅有(n-1)对不能共存,并且对于每种木马,都存在至少一个木马与之不能共存。
老Z不在乎自己AC多少题。请告诉他,他最多能从“善良OJ”上获取木马的个数。
【输入】
第一行,一个正整数n,表示木马个数。
剩余(n-1)行,每行一对木马,表示他们不能共存。(保证相同的木马可以共存,任意不同两行的描述不等价)
木马编号从0至(n-1)
【输入】
一行,老Z最多获得木马的个数。你可以认为开始时没有任何木马。
【输入样例】
3
0 1
1 2
【输出样例】
2
【数据规模】
对于100%的数据,1<=n<=200

/*
不建树.
用链表搞.
双向建边. 
只需要判断father和son是否一样. 
然后裸的树上最大独立集.
用树形DP搞. 
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#define MAXN 1001
using namespace std;
int tot,fa[MAXN][3],w[MAXN],head[MAXN],n;
struct data
{
    int v,next,x;
}e[MAXN];
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
    return x*f;
}
void add(int u,int v)
{
    e[++tot].v=v;
    e[tot].next=head[u];
    head[u]=tot;
}
void dfs(int father,int u)
{
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].v;
        if(v==father) continue;
        dfs(u,v);
        fa[u][0]+=max(fa[v][0],fa[v][1]);
        fa[u][1]+=fa[v][0];
    }
}
int main()
{

    int x,y;
    n=read();
    for(int i=1;i<=n;i++) fa[i][1]=1;
    for(int i=1;i<n;i++)
    {
        x=read();y=read();
        x++;y++;
        add(x,y);add(y,x);
    }
    dfs(0,1);
    printf("%d\n",max(fa[1][0],fa[1][1]));
    return 0;
}
/*
先建树.
不然没法确定点的关系.
然后裸的树上最大独立集.
用树形DP搞. 
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#define MAXN 1001
using namespace std;
int tot,fa[MAXN][3],w[MAXN],head[MAXN],father[MAXN],son[MAXN][MAXN],n;
struct data
{
    int v,next,x;
}e[MAXN];
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
    return x*f;
}
void add(int u,int v)
{
    e[++tot].v=v;
    e[tot].next=head[u];
    head[u]=tot;
}
void build(int x)
{
    for(int i=head[x];i;i=e[i].next)
    {
        int t=e[i].v;
        if(!father[t])
        {
            father[t]=x;
            son[x][++son[x][0]]=t;
            build(t);
        }
    }
}
void dfs(int u)
{
    if(fa[u][1])return;
    fa[u][1]=1;
    for(int i=1;i<=son[u][0];i++)
    {
        int v=son[u][i];
        dfs(v);
        fa[u][0]+=max(fa[v][0],fa[v][1]);
        fa[u][1]+=fa[v][0];
    }
}
int main()
{

    int x,y;
    n=read();
    for(int i=1;i<n;i++)
    {
        x=read();y=read();
        x++;y++;
        add(x,y);
        add(y,x);
    }
    father[1]=1;
    build(1);
    dfs(1);
    printf("%d\n",max(fa[1][0],fa[1][1]));
    return 0;
}
  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值