2025/2/7课堂记录

目录

  1. 二叉苹果树
  2. 选课

这是一道树上依赖背包题,也叫做分组背包

依赖:选这一个,从它到根所有节点都要选,具有依赖性

分组:每个节点下的子节点分好几个组,每一个组单独做背包

maketree:因为题目没告诉父子关系,要自己推

看注释!!就写了亿点点注释
 #include<iostream>
#include<cstring>
using namespace std;
int map[110][110],l[110],r[110],ap[110],f[110][110];
int n,q;
void maketree(int a)
{
	for(int i=1;i<=n;i++)//先找第一个孩子,左右无所谓 
		if(map[a][i]>=0)//找到啦(你的破绽,砰! 
		{
			l[a]=i;//标记左孩子(其实也不一定,但无所谓 
			ap[i]=map[a][i];//所有的苹果放在孩子身上,这步是关键思想!!!!!!!!! 
			map[a][i]=-1;//删边 
			map[i][a]=-1;
			maketree(i);//从孩子接着往下推 
			break;//一定要炸掉,不然l[a]就被顶替了 
		}
	for(int i=1;i<=n;i++)//找第二个(右)孩子,同上 
		if(map[a][i]>=0)
		{
			r[a]=i;
			ap[i]=map[a][i];
			map[a][i]=-1;
			map[i][a]=-1;
			maketree(i);
			break;
		}
}
int dp(int fa,int num)//dp(fa,num)和f[fa][num]的含义都是fa节点必选,总共选num个节点最多苹果数 
{
	if(num==0)return 0;//一个都没有那就一个都没有呗 
	if(l[fa]==0)return ap[fa];//没有孩子那就只有自己呗(此时num肯定>0) 
	if(f[fa][num]>0)return f[fa][num];//之前来过了,不用再来了 
	for(int i=0;i<=num-1;i++)//循环遍历,把num个苹果分成三份,父亲,左边一大串,右边一大串,k用来遍历给左孩子几个 
		f[fa][num]=max(f[fa][num],dp(l[fa],i)+dp(r[fa],num-i-1)+ap[fa]);//右孩子直接计算, 
	return f[fa][num];
}
int main()
{
	cin>>n>>q;//n:节点数量,q:保留边数量 
	q++;//保留q条边,实际就是保留q+1个节点(父亲+每一条边后面跟着一个节点 
	memset(map,-1,sizeof(map));//初始化负数 
	for(int i=1;i<=n-1;i++)	//一颗树,n个结点,n-1条边 
	{
		int x,y,z;
		cin>>x>>y>>z;
		map[x][y]=z;//因为输入的时候父亲孩子没有固定顺序 
		map[y][x]=z;//所以双向建图就行,反正用的链接表,多余空间不用白不用 
	}
	maketree(1);//从根节点一点一点往下推 
	cout<<dp(1,q);//表示:当1节点必须保留的情况下,保留q个节点所能获得的最大苹果数 
}

这同样是一道树上依赖背包题

 用的vector邻接表

然后这道题有好多个树,咋办呢?

然后一个天才般的想法就诞生了:把森林中所有树的根连在一块,汇聚到一个假想的森林的根

然后搬运的老师的代码,稍微写了点注释
 #include <bits/stdc++.h>
using namespace std;
int m,n;
int f[105][105];//f[i][j]:以i作为根,最多选 
vector<int>tr[105];
int a[105];
//分组背包,树上依赖背包:s的每个孩子作为一个分组 
void dfs(int s)
{
	//依次枚举每个孩子子树 
    for(int i=0;i<tr[s].size();i++)
	{
		int y=tr[s][i];
        dfs(y);
        //逆序枚举,类似01背包的压缩实现 
        for(int j=m;j>=0;j--)     //s取j个节点 
		{
            for(int k=j;k>=0;k--)  //y子树取k个节点 
			{
                f[s][j]=max(f[s][j],f[s][j-k]+f[y][k]);
                //y子树取k个,则s的其他子树们累计取j-k个 
            }
        }
    }
    if(s!=0) // s==0的节点是不存在的,不能选 
    {
    	for(int t=m;t>=1;t--)
		{
		    f[s][t]=f[s][t-1]+a[s];	
		} 
	}
}
int main()
{
    cin>>m>>n;
    //这里并没有把树根单独拎出来的原因是把所有树根直接绑在了0上,相当于0是根,所有的“根”都是0的孩子
	//把一片树林看作一颗树 
    for(int i=1;i<=m;i++)
	{
        int x,y;
        cin>>x>>y;
        a[i]=y;
        
        //存储成有向图的方式 
        tr[x].push_back(i);  //x是父亲,i是孩子 
    }
    dfs(0);//森林的根 
    cout<<f[0][n];
    //0作为子树,连续选择n个节点 
    return 0;
}

 

posted @   永韶  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek-R1本地部署如何选择适合你的版本?看这里
· 开源的 DeepSeek-R1「GitHub 热点速览」
· 传国玉玺易主,ai.com竟然跳转到国产AI
· 揭秘 Sdcb Chats 如何解析 DeepSeek-R1 思维链
· 自己如何在本地电脑从零搭建DeepSeek!手把手教学,快来看看! (建议收藏)
点击右上角即可分享
微信分享提示