样例输入:
- 2
- 5
- 1 2
- 2 3
- 3 4
- 4 5
- 10
- 1 2
- 2 4
- 3 2
- 5 3
- 6 4
- 7 5
- 8 3
- 9 8
- 10 7
-
样例输出:
- 4
- 7
题意:求一棵树的最大分离集的大小,无向图的分离集是这样的一组顶点,如果我们只保留这些顶点之间的边,则集合中的每个顶点至多连接一条边,而分离集的大小就是顶点数目。
分析:这道题显然是树形DP,接下来我用两种实现方式给大家讲解,一种是我在考试时想到的一种三维树形DP,另一种是题解中阐述的二维树形DP。
先来说一下三位树形DP的实现方式:
设f[i][1/0][1/0]表示以第i个节点为根的子树中选取/不选取第i个点,选取/不选取孩子节点的分离集的最大值。
知道了状态表示那么状态转移就好分析了。
对于f[i][1][0]和f[i][0][0]这两个状态最好转移,因为这都是孩子节点不作为分离集中顶点的情况,那么对于每一个孩子节点j,我们都取max(f[j][0][0],f[j][0][1])即可
而对于f[i][1][1]和f[i][0][1]这两个状态的转移分析就稍微有点难度了,先来看一下f[i][1][1],这里代表了第i个节点作为分离集中的一个顶点,且第i个点的某个孩子节点也作为分离集中顶点的情况,需要注意的一点是因为我们选取了第i个节点,所以第i个节点的孩子节点中只能有一个节点也同属于分离集中,这是分离集的定义所规定的,而且由于状态表示的定义,孩子节点必须有一个在分离集中,所以我们对于所有的孩子节点,必须要使得某一个孩子节点j选取f[i][1][0],其他孩子节点k都必须选取max(f[k][0][0],f[k][0][1]),现在关键我们要令哪个孩子节点属于分离集中呢?因为对于每一个孩子节点我们要么在分离集中,要么不在分离集中,所以我们就需要找到一个f[k][1][0]-max(f[k][0][0],f[k][0][1])的最大值的点,然后让这个点在分离集中即可,因为如果这个点不在分离集中我们相较于其他点损失会更大,而其他孩子节点k直接取max(f[k][0][0],f[k][0][1])即可。下面来分析一下f[i][0][1]的情况,这是对应的第i个节点不在分离集中而孩子节点至少有一个在分离集中的情况,那这个对于i的每个孩子j而言,我们需要取max(max(f[j][1][1],f[j][1][0]),max(f[j][0][1],f[j][0][0])),但是同时我们还要保证一个条件就是至少有一个孩子节点在分离集中,这个孩子的选取方法与上面的分析方法类似,就是选取max(f[j][1][1],f[j][1][0])-max(f[j][0][1],f[j][0][0])最大的那个孩子节点,但是需要注意的一点是这种情况并不是要求只能有一个孩子节点在分离集中,而是要求至少有一个孩子在分离集中,所以这也是与上面那种情况不同的地方,细节见代码:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- using namespace std;
- const int N=5e5+10;
- int h[N],ne[N*10],e[N*10],idx;
- int f[N][2][2];
- void add(int x,int y)
- {
- e[idx]=y;
- ne[idx]=h[x];
- h[x]=idx++;
- }
- void dfs(int x,int fa)
- {
- f[x][1][0]=f[x][1][1]=1;
- f[x][0][1]=f[x][0][0]=0;
- int cha1=-199999999,cha2=-199999999;
- for(int i=h[x];i!=-1;i=ne[i])
- {
- int j=e[i];
- if(j==fa) continue;
- dfs(j,x);
- f[x][0][1]+=max(max(f[j][1][1],f[j][1][0]),max(f[j][0][1],f[j][0][0]));
- cha1=max(cha1,max(f[j][1][1],f[j][1][0])-max(f[j][0][1],f[j][0][0]));
-
- f[x][0][0]+=max(f[j][0][0],f[j][0][1]);
- f[x][1][0]+=max(f[j][0][0],f[j][0][1]);
-
- f[x][1][1]+=max(f[j][0][1],f[j][0][0]);
- cha2=max(cha2,f[j][1][0]-max(f[j][0][1],f[j][0][0]));
- }
- if(cha1<0) f[x][0][1]+=cha1;
- f[x][1][1]+=cha2;
- }
- int main()
- {
- int size(512<<20); // 512M
- __asm__ ( "movq %0, %%rsp\n"::"r"((char*)malloc(size)+size));
- int T;
- cin>>T;
- while(T--)
- {
- int n;
- scanf("%d",&n);
- idx=0;
- for(int i=1;i<=n;i++) h[i]=-1;
- for(int i=1;i
- {
- int u,v;
- scanf("%d%d",&u,&v);
- add(u,v);add(v,u);
- }
- dfs(1,-1);
- printf("%d\n",max(max(f[1][0][0],f[1][0][1]),max(f[1][1][0],f[1][1][1])));
- }
- exit(0);
- }
接下来我说一下二维的做法。
DP状态表示:
f[i][0]代表以i为根的子树中i未选时的符合题意的最大分离集大小
f[i][1]代表以i为根的子树中i被选且在分离集中度数为0时的符合题意的最大分离集大小
f[i][2]代表以i为根的子树中i被选且在分离集中度数为1时的符合题意的最大分离集大小
下面直接说一下状态更新,对于f[i][0]很容易更新,因为他代表不选第i个节点的情况,那么子节点是什么情况都可以,所以对于每一个孩子节点j直接取f[j][0],f[j][1],f[j][2]三者的最大值即可。f[i][1]更新也比较容易,因为他代表只选第i个节点而不选第i个节点的孩子节点的情况,所以对于每一个孩子节点j只需要取所有的f[j][0]即可。最主要的是f[i][2]的更新,因为这个是要选一个孩子节点作为分离集内部顶点的,而且只能选一个孩子节点作为分离集内部顶点,所以选哪个节点是我们需要考虑的问题,对于不作为分离集内部节点的孩子节点j我们就取f[j][0],作为分离集内部节点的那个孩子节点k我们就取f[k][1],至于怎么选哪个孩子作为分离集内部顶点类似于三维DP时的那种分析方法,选f[j][1]-f[j][0]值最大的孩子节点j作为分离集内部的孩子节点即可。
下面是代码:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- using namespace std;
- const int N=5e5+10;
- int h[N],ne[N*10],e[N*10],idx;
- int f[N][3];
- //f[i][0]代表以i为根的子树中i未选时的符合题意的最大分离集大小
- //f[i][1]代表以i为根的子树中i被选且在分离集中度数为0时的符合题意的最大分离集大小
- //f[i][2]代表以i为根的子树中i被选且在分离集中度数为1时的符合题意的最大分离集大小
- void add(int x,int y)
- {
- e[idx]=y;
- ne[idx]=h[x];
- h[x]=idx++;
- }
- void dfs(int x,int fa)
- {
- f[x][0]=0;
- f[x][1]=f[x][2]=1;
- int cha=-199999999;
- for(int i=h[x];i!=-1;i=ne[i])
- {
- int j=e[i];
- if(j==fa) continue;
- dfs(j,x);
- f[x][0]+=max(f[j][0],max(f[j][1],f[j][2]));
- f[x][1]+=f[j][0];
- f[x][2]+=f[j][0];
- cha=max(cha,f[j][1]-f[j][0]);
- }
- f[x][2]+=cha;
- }
- int main()
- {
- int size(512<<20); // 512M
- __asm__ ( "movq %0, %%rsp\n"::"r"((char*)malloc(size)+size));
- int T;
- cin>>T;
- while(T--)
- {
- int n;
- scanf("%d",&n);
- idx=0;
- for(int i=1;i<=n;i++) h[i]=-1;
- for(int i=1;i
- {
- int u,v;
- scanf("%d%d",&u,&v);
- add(u,v);add(v,u);
- }
- dfs(1,-1);
- printf("%d\n",max(f[1][0],max(f[1][1],f[1][2])));
- }
- exit(0);
- }
-
相关阅读:
解决svn update 产生Node remains in conflict的报错问题
JVM-Java虚拟机内存区域
【Python】绘制 指数函数 || 同一个坐标系绘制多个指数函数 || 数学的魅力
Spring cloud day(8) stream
javaEE幼儿园学生管理系统
(Spring笔记)AspectJ后置通知——@AfterReturning切面开发
看看面试要价25K的软件测试工程师,技术面都问他些什么问题?我上我也行
C++内存模型与名称空间总结,看这一篇就够了
当Github启用PSA之后...
Learn Prompt-基础用法
-
原文地址:https://blog.csdn.net/AC__dream/article/details/126295443