2025/2/10课堂记录
目录
- 选课
- 叶子的染色
- 数字转换
分组背包题
这次是自己写的代码了,也就瞟了标准答案几眼,真的就几眼
用的也是vector邻接表
#include<iostream>
#include<vector>
using namespace std;
vector <int>zi[110];//zi[i][j]:i的第j个孩子 (j动态大小
int a[110],m,n,f[110][110];//f[i][j]:以i为根最多选j和节点,最大值
void maketree(int root)
{
for(int i=0;i<zi[root].size();i++)
{
maketree(zi[root][i]);
for(int j=m;j>=1;j--)
for(int k=j;k>=1;k--)
f[root][j]=max(f[root][j],f[root][j-k]+f[zi[root][i]][k]);
}
if(root!=0)
for(int i=m;i>=1;i--)
f[root][i]=f[root][i-1]+a[root];
}
int main()
{
cin>>m>>n;//m:待选,n:可选
for(int i=1;i<=m;i++)
{
int x,y;
cin>>x>>y;
zi[x].push_back(i);//表示x的孩子再多一个i
a[i]=y;
}
maketree(0);
cout<<f[0][n];
return 0;
}
话说vector真的比链式前向星好用唉
这是一道树形dp题
然后代码简单题难懂,主要解释一下题是啥意思
首先有一颗树,总共有m个节点,n个叶子结点
这n个叶子结点的编号是1-n
这棵树的根就是在n+1 - m这个区间随便选一个,代码上选的是n+1这个节点
其实题目里面告诉了是无根树,那么随便选哪一个非叶子节点当根结果都是一样的
至于样例,那么以n+1即节点4为树根进行演示
可以看到,只要将4染黑,2染白,就能达到1黑2白3黑的效果
因为4可以扩散到5、1,5可以再扩散到2、3,但2已经染过色了
代码用的链式前向星
剩下代码就很简单了,看看注释吧
#include<iostream>
using namespace std;
int c[10010],tot,f[100010][2],head[10010],m,n;
//f[i][0/1]:以i点作为子树树根,i点染成黑/白色时,整颗子树最小值
struct EDGE
{
int next,to;
}edge[100010];
void add(int from,int to)
{
edge[++tot].to=to;
edge[tot].next=head[from];
head[from]=tot;
}
void dp(int root,int from)
{
f[root][0]=1,f[root][1]=1;//染色就+1,后来会减掉
if(root<=n)//当前子树根节点为叶子结点
if(c[root]==1)//白色
f[root][0]=0x3f3f3f3f;//肯定不可能是黑色
else//黑色
f[root][1]=0x3f3f3f3f;
for(int i=head[root];i;i=edge[i].next)//遍历所有相连的点
if(edge[i].to!=from)//父亲除外,那就遍历所有孩子
{
dp(edge[i].to,root);//每一个孩子节点作为子树树根,此节点作为父亲
f[root][0]+=min(f[edge[i].to][0]-1,f[edge[i].to][1]);
//如果root是黑色,他的孩子也是黑色的话,他孩子的那个染色就可以褪掉了,反之孩子还得染着色
f[root][1]+=min(f[edge[i].to][1]-1,f[edge[i].to][0]);
}
}
int main()
{
cin>>m>>n;
for(int i=1;i<=n;i++)cin>>c[i];
for(int i=1;i<=m-1;i++)
{
int x,y;
cin>>x>>y;
add(x,y);
add(y,x);
}
dp(n+1,-1);//随便选一个不是叶子的节点都行,没有父亲为-1
cout<<min(f[n+1][1],f[n+1][0]);//贪心思想,根节点肯定要染色
// cout<<"\n"; //test
// for(int i=1;i<=m;i++)cout<<f[i][0]<<" "<<f[i][1]<<"\n"; //test
return 0;
}
另外还有点贪心思想在里面,即根节点肯定要染色
-
数字转换(作业)
这是一道树的直径题
树的直径,标准模板
// 树的直径 dp实现;
// https://blog.csdn.net/qq_42211531/article/details/86579115
// dp[u][0]: 结点u的最长儿子链
// dp[u][1]: 结点u的次长儿子链
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int const MAX = 100005;
int head[MAX], dp[MAX][2];
int n, s, cnt, ans;
struct EDGE
{
int v, w, next;
}e[MAX];
void Add(int u, int v, int w)
{
e[++cnt].v = v;
e[cnt].w = w;
e[cnt].next = head[u];
head[u] = cnt;
}
// 利用孩子的最长链去更新父亲的最长链和次长链
void DFS(int u, int fa)
{
//dp[u][0]:最长子链; dp[u][1]:次长子链
dp[u][0] = dp[u][1] = 0;
for(int i = head[u]; i ; i = e[i].next)
{
int v = e[i].v;
int w = e[i].w;
if(v != fa)
{
DFS(v, u);
if(dp[u][0] < dp[v][0] + w) // 父亲u的最长 < 孩子v的最长 + (u,v)边长
{
dp[u][1]= dp[u][0];
dp[u][0] = dp[v][0] + w;
}
else
if(dp[u][1] < dp[v][0] + w)
dp[u][1] = dp[v][0] + w;
}
}
//枚举经过每个节点的长链,是否最大?
ans = max(ans, dp[u][1] + dp[u][0]);
}
int main()
{
memset(head, 0, sizeof(head));
scanf("%d", &n);
for(int i = 1; i <= n - 1; i++)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
Add(u, v, w);
Add(v, u, w);
}
DFS(1, -1); //假设1作为无根树的树根;
printf("%d\n",ans);
}
树的直径二次dp
//https://blog.csdn.net/Rainfoo/article/details/105290837
//图论-树-最长链(树的直径)
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<stack>
using namespace std;
#define ll long long
const int maxn=2e5+5;
int d[maxn],head[maxn],f_num,ans,tot;
struct E{
int to,next,w;
}edge[maxn];
void add(int u,int v,int z){
edge[tot].to=v;
edge[tot].w=z;
edge[tot].next=head[u];
head[u]=tot++;
}
void dfs(int x,int fa){
if(ans<d[x]){
ans=d[x];
f_num=x;
}
for(int i=head[x];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(v==fa) continue;
d[v]=d[x]+edge[i].w;
dfs(v,x);
}
}
int main(){
memset(head,-1,sizeof(head));
int n,m;cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v,w;cin>>u>>v;
//cin>>w;
add(u,v,w);add(v,u,w);
}
dfs(1,0);
ans=0;
d[f_num]=0;
dfs(f_num,0);
cout<<ans<<endl;
}
这个是数字转换的答案
#include<iostream>
using namespace std;
#define LL long long
#define ULL unsigned long long
const int INF=0x3f3f3f;
const double eps=1e-5;
const int maxn=5e4+10;
/*题意:若是一个数x的所有约数(不包括他自己)之和sum比他自己小,
那么x可以转化成sum,sum也可以成 x。例如 4可以变为 3,1可以变为7
限制所有数字变换在不跨越 n的正整数范围内举行转化,求不停举行数字变换且无重复数字的最多变换步数*/
int sum[maxn];//预处理每个数的约数之和
int f1[maxn],f2[maxn];//f1:以i为根的树中,i到叶子节点的最长距离,f2 :....次长距离
//直径就是 max(f1[i]+f2[i]) 就是树中所有的两点最短距离中的最大值
//
int n;
void getsum()//预处理每个数的约数之和
{
for(int i=1;i<=n;i++)
{
for(int j=2;j<=n/i;j++)
{
sum[i*j]+=i; //i是i*j的约数
}
}
}
int main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
// cout.tie(0);
scanf("%d",&n);
getsum();
for(int i=n;i>=1;i--)
{
if(sum[i]>=i)
continue;
//把sum[i]看成i的父亲,因为每个i的sum[i]都是唯一的 而能变成i的数不唯一
if(f1[i]+1>f1[sum[i]])
{
f2[sum[i]]=f1[sum[i]];
f1[sum[i]]=f1[i]+1;
}
else
if(f1[i]+1>f2[sum[i]])
f2[sum[i]]=f1[i]+1;
}
int ans=-1;
for(int i=1;i<=n;i++)
ans=max(ans,f1[i]+f2[i]);
printf("%d\n",ans);
return 0;
}
好不容易上了一节课,上着上着把烦恼忘了,这下好了,今晚又睡不着了
本文来自博客园,作者:永韶,转载请注明原文链接:https://www.cnblogs.com/yongshao/p/18708927
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek-R1本地部署如何选择适合你的版本?看这里
· 开源的 DeepSeek-R1「GitHub 热点速览」
· 传国玉玺易主,ai.com竟然跳转到国产AI
· 揭秘 Sdcb Chats 如何解析 DeepSeek-R1 思维链
· 自己如何在本地电脑从零搭建DeepSeek!手把手教学,快来看看! (建议收藏)