1 条题解

  • 0
    @ 2025-2-14 21:20:42

    C++ :

    //此题是一个典型的树归,因为可能多门课程共用同一个先决课程,所以这是一颗普通树
    //同时有些科目可能没有先决课程,这样就可能是一个森林,一个森林是没法用树归的,
    //那我们可以让不同的子树以0为根节点,形成一颗树,其中0为虚根,其学分为零,这样
    //这样我可以枚举以0点为根的子树,选n门课时的最大值,但是这样做需要枚举根的每一个
    //儿子,而且不同根,它的儿子树也不相同,所以做起来比较复杂,我们在学习树形结构时
    //掌握了普通树转二叉树的方法,原则是左儿子保留不变,兄弟变右儿子,这样转了后我们
    //设f[i][j]表示以i节点为根,选j门课程的最大学分,那么,对i的左儿子来说i是必选课程,
    //而右儿子是i的兄弟,没有必选的关系,所以选择根i节点的话,那么可以对左子树选k门
    //右儿子选j-k-1门,要么就是只选右子树。
    //f[i][j]=max{f[i_right][j],f[i_left][k]+f[i_right][j-k-1]+a[i]} 
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int maxn=1001;
    struct tre
    {
    	int lc,rc;
    };
    int m,n,f[maxn][maxn]={0},a[maxn];
    tre tree[maxn];
    void init();
    int my_max(int,int);
    void insert(int,int);
    int dp(int,int);
    int main()
    {
    	init();
    	cout<<dp(tree[0].lc,n)<<endl;//根0为虚根,所以其没有右子树,结果从其左子树找n门课程 
    	return 0;
    }
    void init()
    {
    	memset(tree,0,sizeof(tree));
    	cin>>m>>n;
    	for(int i=1;i<=m;i++)
    	{
    		int fa;//fa为i的直接必选课程 
    		cin>>fa>>a[i];
    		insert(i,fa);//建树 
    	}
    }
    void insert(int son,int father)
    {
    	if(!tree[father].lc)//如果父亲节点没有左儿子则,该点为其左儿子 
    	{
    		tree[father].lc=son;
    	}
    	else//如果有左儿子,则成为相邻兄弟的右儿子 
    	{
    		int t=tree[father].lc;
    		while(tree[t].rc) t=tree[t].rc;//递归找到第一个没有右儿子的节点 
    		tree[t].rc=son;
    	}
    }
    int dp(int x,int y)
    {
    	if(f[x][y]>0)return f[x][y];//如果有值则返回 
    	if(x==0||y==0)//x==0表明该节点没有子树,y为0表明不选任何课程 
    	{
    		f[x][y]=0;
    		return f[x][y];
    	}
    	f[x][y]=dp(tree[x].rc,y);//先假设不选根x节点,则只选其右子树 
    	for(int i=0;i<=y-1;i++)//如果选根x节点,左儿子选i个节点,i从[0,y-1] 
    	{
    		f[x][y]=my_max(f[x][y],dp(tree[x].lc,i)+dp(tree[x].rc,y-i-1)+a[x]);
    	}
    	return f[x][y];
    }
    int my_max(int x,int y)
    {
    	return x>y ? x:y;
    }
    

    Pascal :

    var i,j,n,m,w:longint;
    ans,f:Array[-1..200,-1..200] of longint;
      k,s,le,ri:Array[0..200] of longint;
     function  max(a,b:longint):longint;
      begin
       if a>b then exit(a) else exit(b);
      end;
     procedure work(num:longint);
      var i,j:longint;
       begin
        if num=-1 then exit;
        if
        (le[num]=-1) and (ri[num]=-1) then begin
         for i:=1 to m do
          ans[num,i]:=s[num];
          exit;
          end;
        work(le[num]);
        work(ri[num]);
        for i:=1 to m do
         begin
          ans[num,i]:=max(ans[num,i],ans[ri[num],i]); end;
        if num=0 then exit;
    
        for i:=0 to m-1 do
         for j:=0 to i do
           ans[num,i+1]:=max(ans[num,i+1],ans[le[num],j]+ans[ri[num],i-j]+s[num]);
    
    
       end;
     begin
      readln(n,m);
      for i:=1 to n do
        begin
         read(k[i],s[i]);
        inc(f[k[i],0]);
        f[k[i],f[k[i],0]]:=i;
        end;
      for i:=0 to n do
       begin
        le[i]:=-1;
        ri[i]:=-1;
       end;
      for i:=0 to n do
        if f[i,0]<>0 then
         begin
    
          le[i]:=f[i,1];
          w:=f[i,1];
          for j:=2 to f[i,0] do
           begin
            ri[w]:=f[i,j];
            w:=f[i,j];
         end;
         end;
    
      //write(le[0]);
      work(0);
      write(ans[le[0],m]);
     // for i:=0 to m do
     //  begin
     //   f[0,m]:=max(f[0,m],f[le[0],i]+f[ri[0],m-i]);
      //  write(f[0,m],' ');
    //  end;
     //  write(f[le[0],m]);
    
    
    
     end.
    
    
    • 1

    信息

    ID
    841
    时间
    1000ms
    内存
    128MiB
    难度
    (无)
    标签
    递交数
    0
    已通过
    0
    上传者