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;
}