2025/2/7课堂记录
目录
- 二叉苹果树
- 选课
这是一道树上依赖背包题,也叫做分组背包
依赖:选这一个,从它到根所有节点都要选,具有依赖性
分组:每个节点下的子节点分好几个组,每一个组单独做背包
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;
}
本文来自博客园,作者:永韶,转载请注明原文链接:https://www.cnblogs.com/yongshao/p/18703333
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek-R1本地部署如何选择适合你的版本?看这里
· 开源的 DeepSeek-R1「GitHub 热点速览」
· 传国玉玺易主,ai.com竟然跳转到国产AI
· 揭秘 Sdcb Chats 如何解析 DeepSeek-R1 思维链
· 自己如何在本地电脑从零搭建DeepSeek!手把手教学,快来看看! (建议收藏)