森林转二叉树(Vijos1180 选课)

Vijos1180 选课
描述

学校实行学分制。每门的必修课都有固定的学分,同时还必须获得相应的选修课程学分。学校开设了N(N<300)门的选修课程,每个学生可选课程的数量M是给定的。学生选修了这M门课并考核通过就能获得相应的学分。
在选修课程中,有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其它的一些课程的基础上才能选修。例如《Frontpage》必须在选修了《Windows操作基础》之后才能选修。我们称《Windows操作基础》是《Frontpage》的先修课。每门课的直接先修课最多只有一门。两门课也可能存在相同的先修课。每门课都有一个课号,依次为1,2,3,…。 例如:
表中1是2的先修课,2是3、4的先修课。如果要选3,那么1和2都一定已被选修过。   你的任务是为自己确定一个选课方案,使得你能得到的学分最多,并且必须满足先修课优先的原则。假定课程之间不存在时间上的冲突。
格式

输入
输入文件的第一行包括两个整数N、M(中间用一个空格隔开)其中1≤N≤300,1≤M≤N。
以下N行每行代表一门课。课号依次为1,2,…,N。每行有两个数(用一个空格隔开),第一个数为这门课先修课的课号(若不存在先修课则该项为0),第二个数为这门课的学分。学分是不超过10的正整数。

输出
输出文件每行只有一个数。第一行是实际所选课程的学分总数。
样例1

样例输入1
7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2
样例输出1
13

这道题是不是比较恶心?
这和我们之前做的题不同,他这里不是二叉树,而是多叉树。
而且….
可能A节点有5个儿子,而B节点只有1个儿子..
这么恶心的东东,就要有恶心的算法来对付他
在发代码之前,我们先说一个森林转二叉树的东东

森林里面有什么?树啊
这么多的树我们要把它转成一棵二叉树?
这里写图片描述

不急,我们先将将一棵树转为一棵二叉树

这是一棵树:
这里写图片描述
对你没看错这是一棵可(恶)爱(心)的树树

这里写图片描述
我们给同级的兄弟们加上了线
让他们成为了一个整体

这里写图片描述
我们给除了跟第一个左孩子
和与你同级的第一个左孩子
连线
剩下的线全部去掉!!!
嗯这些线贼恶心我们去掉去掉

这里写图片描述

整理一波 二叉树耶!

至于森林转二叉树

这里写图片描述
首先这是一片森林(是的我没逗你)

这里写图片描述
然后我们按刚刚的方法修建一下这些树树
变成几棵二叉树

这里写图片描述
以第一棵树为根节点
每一棵转化而来的二叉树都作为前一棵树的右孩子
然后
神奇的事情发生了
二叉树!!

然而刚才讲这么多
好像
好像对于这道题并没有什么卵用得
是的我们只需要用到第一步就够了
代码:

#include <cstdio>
#include <cstring>
using namespace std;
struct node
{
    int lc,rc,c,v;
    //lc(左儿子):他真正的儿子
    //rc(右儿子):他的兄弟
    node()
    {
        lc=rc=-1;c=0;v=0;
    }
}a[11000];
int f[1100][1100];
int mymax(int x,int y) 
{
    return x>y?x:y;
}
int treedp(int x,int y)
{
    if (x<0||y<0) return 0;
    if (f[x][y]!=-1) return f[x][y];
    int maxx=0;
    for (int i=0;i<=y;i++)//枚举
    {
        int ls=y-i-1,rs=i;
        //分成左右两部分,左边ls分,右边rs分
        int lss=0,rss=0;
        if (a[x].lc!=-1) lss=f[a[x].lc][ls]=treedp(a[x].lc,ls);
        if (a[x].rc!=-1) rss=f[a[x].rc][rs]=treedp(a[x].rc,rs);
        if (ls<0) maxx=mymax(maxx,rss);
        else maxx=mymax(maxx,lss+rss+a[x].c);
    }
    return maxx;
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
    {
        int x;
        scanf("%d%d",&x,&a[i].c);
        //讲这么多其实写出来就一个if语句
        if (a[x].v==0) a[x].lc=i;//如果他没有儿子就把它收为他的左儿子
        else a[a[x].v].rc=i;//否则就把它作为他儿子的兄弟(就是他左儿子的右儿子)
        a[x].v=i;
    }
    memset(f,-1,sizeof(f));
    for (int i=1;i<=n;i++)
        f[i][0]=0;
    printf("%d\n",treedp(0,m+1));
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值