本题有两种写法,dfs树上DP和仙人掌DP。

先考虑dfs树DP。

什么是dfs树?其实是对于一棵仙人掌,dfs后形成生成树,找出非树边(即返祖边),然后dfs后每条返祖边+其所覆盖的链构成了一个环(很显然覆盖的链互不相交),然后可以确定每条边出现在哪个环中,然后可以解决一些简单的仙人掌DP问题,不用写tarjan了。

这道题的第一种方法就是dfs树DP,题目是求仙人掌的最大独立集。

首先树形DP,没有环应该很好求,有环的情况,考虑记录环上的点的top和end(注意环顶部不用记录,因为环顶部可能属于另一个环底)。然后可以直接DP了,相比于树形DP多记录一维环底部是否选,改成开两个数组,g[i][0/1]表示强制让环底部不选,该点不选/选的最大值,f[i][0/1]表示无所谓让环底部选不选,该点不选/选的最大值。首先对点u,初始化:f[u][1]=1,若不为环底部,则g[u][1]=1。dfs到边u->v,g[u][1]+=g[v][0],因为无论如何该点选了则儿子、底部势必不选,如果两点不在一个环上,则g[u][0]+=max(f[u][0],f[u][1]),反之g[u][0]+=max(g[v][0],g[v][1])。然后考虑转移f数组,首先f[u][0]+=max(f[v][0],f[v][1]),因为u不选,一切自由,如果u不为环的顶部,则f[u][1]+=f[v][0],反之,底部也不能选,f[u][1]+=g[v][0]。

贴不贴code也无所谓了,都说了这么多,不过还是贴一个吧。

#include<bits/stdc++.h>
using namespace std;
const int N=;
int n,m,dep[N],fa[N],tp[N],ed[N],f[N][],g[N][];
vector<int>G[N];
void dfs(int u)
{
dep[u]=dep[fa[u]]+;
for(int i=;i<G[u].size();i++)if(!dep[G[u][i]])fa[G[u][i]]=u,dfs(G[u][i]);
}
void walk(int u,int v){int x=v;while(x!=u)tp[x]=u,ed[x]=v,x=fa[x];}
void dp(int u)
{
f[u][]=;
if(u!=ed[u])g[u][]=;
for(int i=;i<G[u].size();i++)
if(dep[u]+==dep[G[u][i]])
{
int v=G[u][i];
dp(v);
if(ed[u]!=ed[v])g[u][]+=max(f[v][],f[v][]);else g[u][]+=max(g[v][],g[v][]);
g[u][]+=g[v][];
if(tp[v]!=u)f[u][]+=f[v][];else f[u][]+=g[v][];
f[u][]+=max(f[v][],f[v][]);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=,x,y;i<=m;i++)scanf("%d%d",&x,&y),G[x].push_back(y),G[y].push_back(x);
dfs();
for(int u=;u<=n;u++)
for(int i=;i<G[u].size();i++)
if(dep[u]+<dep[G[u][i]])walk(u,G[u][i]);
dp();
printf("%d",max(f[][],f[][]));
}

这题也可以仙人掌DP,当然不需要建立圆方树。tarjan的本质是构建dfs树,然后f[i][0/1]表示当前节点为i,不选/选的最大值,当边是树边时可以直接转移,反之暂时不转移。等到发现其为环的顶部时,然后从底部向顶部推一遍答案。由于是一条链,维护f0和f1表示当前其不选/选的最大值,然后推两遍即可,注意推一遍求f[u][1]时,强制不选底部,即f1=-inf,然后就是裸的树形DP了

#include<bits/stdc++.h>
using namespace std;
const int N=;
int n,m,cnt,dfn[N],low[N],fa[N],f[N][];
vector<int>G[N];
void dp(int u,int v)
{
int t0,t1,f0=,f1=;
for(int i=v;i!=u;i=fa[i])t0=f0+f[i][],t1=f1+f[i][],f0=max(t0,t1),f1=t0;
f[u][]+=f0;
f0=,f1=-1e9;
for(int i=v;i!=u;i=fa[i])t0=f0+f[i][],t1=f1+f[i][],f0=max(t0,t1),f1=t0;
f[u][]+=f1;
}
void tarjan(int u,int pre)
{
fa[u]=pre,dfn[u]=low[u]=++cnt;
f[u][]=,f[u][]=;
for(int i=;i<G[u].size();i++)
{
int v=G[u][i];
if(!dfn[v])tarjan(v,u),low[u]=min(low[u],low[v]);
else if(v!=pre)low[u]=min(low[u],dfn[v]);
if(low[v]>dfn[u])f[u][]+=f[v][],f[u][]+=max(f[v][],f[v][]);
}
for(int i=;i<G[u].size();i++)if(fa[G[u][i]]!=u&&dfn[u]<dfn[G[u][i]])dp(u,G[u][i]);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=,x,y;i<=m;i++)scanf("%d%d",&x,&y),G[x].push_back(y),G[y].push_back(x);
tarjan(,);
printf("%d",max(f[][],f[][]));
}

bzoj4316小C的独立集(dfs树/仙人掌+DP)的更多相关文章

  1. [BZOJ4316]小C的独立集 仙人掌?

    题目链接 因为xls让我每周模拟一次,然后学习模拟中没有学过的东西.所以就来学圆方树. 本来这道题用不着圆方树,但是圆方树是看yyb的博客学的,他在里面讲一下作为一个引子,所以也来写一下. 首先来Ta ...

  2. 【题解】Bzoj4316小C的独立集

    决定要开始学习圆方树 & 仙人掌相关姿势.加油~~ 其实感觉仙人掌本质上还是一棵树,长得也还挺优美的.很多的想法都可以往树的方面上靠,再针对仙人掌的特性做出改进.这题首先如果是在树上的话那么实 ...

  3. 2018.07.08 hdu4521 小明系列问题——小明序列(线段树+简单dp)

    小明系列问题--小明序列 Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others) Proble ...

  4. [BZOJ4316]小C的独立集(圆方树DP)

    题意:求仙人掌图直径. 算法:建出仙人掌圆方树,对于圆点直接做普通的树上DP(忽略方点儿子),方点做环上DP并将值直接赋给父亲. 建图时有一个很好的性质,就是一个方点在邻接表里的点的顺序正好就是从环的 ...

  5. BZOJ4316 小C的独立集 【仙人掌】

    题目 图论小王子小C经常虐菜,特别是在图论方面,经常把小D虐得很惨很惨. 这不,小C让小D去求一个无向图的最大独立集,通俗地讲就是:在无向图中选出若干个点,这些点互相没有边连接,并使取出的点尽量多. ...

  6. 2019.02.07 bzoj4316: 小C的独立集(仙人掌+树形dp)

    传送门 题意:给出一个仙人掌森林求其最大独立集. 思路:如果没有环可以用经典的树形dpdpdp解决. fi,0/1f_{i,0/1}fi,0/1​表示第iii个点不选/选的最大独立集. 然后fi,0+ ...

  7. bzoj4316: 小C的独立集

    Description 图论小王子小C经常虐菜,特别是在图论方面,经常把小D虐得很惨很惨. 这不,小C让小D去求一个无向图的最大独立集,通俗地讲就是:在无向图中选出若干个点,这些点互相没有边连接,并使 ...

  8. 树上独立集数量 树型DP

    题目描述: 对于一棵树,独立集是指两两互不相邻的节点构成的集合.例如,图1有5个不同的独立集(1个双点集合.3个单点集合.1个空集),图2有14个不同的独立集,图3有5536个不同的独立集.  输入: ...

  9. hdu 4521 小明序列(线段树,DP思想)

    题意: ①首先定义S为一个有序序列,S={ A1 , A2 , A3 , ... , An },n为元素个数 : ②然后定义Sub为S中取出的一个子序列,Sub={ Ai1 , Ai2 , Ai3 , ...

随机推荐

  1. C++面试常见问题——10派生类的构造与析构

    派生类的构造与析构 派生类的构造 派生时构造函数与析构函数不会被继承,需要重新定义派生类的构造函数与析构函数.派生类对象包含了基类对象的值,创建派生类时首先会调用基类的构造函数,若派生类中含有其它类对 ...

  2. Andorid 搭建 Linux服务器(一)

    00.搭建环境 电脑系统:MacOS下Win7虚拟机 手机型号:红米Note5A 手机系统:MIUI10开发版 软件: SuperSU      --通过recovery刷入,管理ROOT权限 Bus ...

  3. 012、MySQL取本月第一天日期,取每个月的第一天日期

    #取本月第一天 SELECT DATE_ADD( curdate( ), INTERVAL DAY ); #取往后几个月的第一天 , INTERVAL MONTH ); , INTERVAL MONT ...

  4. CSS隐藏商务通等内容

    CSS隐藏商务通等内容<style>#qiao-wrap{display:none !important;} </style>

  5. C# 关于AD域的操作 (首博)

    前段时间(因为懒得找具体的时间了)公司说让系统可以进行对AD域的操作,包括创建用户.于是上网查资料,了解何为AD域.还不知道的这边请https://www.cnblogs.com/cnjavahome ...

  6. Adapter之自定义Adapter

    前言: 在我们写程序是经常会用到适配器,当系统自带的适配器不够用时即可自己定义适配器 正文: 因为我们用到的ListView视图所以我们先初始化ListView,在我们的主活动中添加如下代码 < ...

  7. SYSTEMTIME 获取日期之差

    #include <windows.h> #include <stdio.h> #include <stdlib.h> #include <string.h& ...

  8. WTL之VS2013环境搭建

    新版博客已经搭建好了,有问题请访问 htt://www.crazydebug.com 从国外回来,在老家入职了新公司,做c++开发,刚到新公司要熟悉公司的项目代码,目前公司在做一个主播聚合平台,界面采 ...

  9. vim快速跳转到某一行

    在vim命令行模式下输入 : n(行数)  |

  10. layui-注册界面

    注册页面register.html源代码: <!DOCTYPE html> <html lang="en"> <head> <meta c ...