题意:给定一棵仙人掌图,求其最大独立集。
独立集:点集内任意两点不存在边直接相连。
点相互独立:这些点组成的点集为独立集
题解:
树上情况
:dp[i][0/1]表示考虑i为根的子数,i不选/选最多能选几个点。设j为i的儿子,有转移方程:
非退化情况
:求出圆方树,
圆点
使用上面的方法进行转移。对于一个
方点
x,我们需要明确dp[x][0/1]的实际意义,然后才可以进行转移。
方点代表一个环,且树的结构为:选择环的一个点作为环的父亲,这个点向方点连接一条有向边,方点向环上剩下的点依次连接有向边,即按照下图的方式:
先考虑第二个转移方程,对于环的根u而言,方点x为u的儿子,故而dp[x][0]被累加到dp[u][1]中,而dp[u][1]的意义是,u必选,因此方点不能对u的选择产生干扰,参考上图,不产生干扰的选择方式为:链两端的点不能选择。
因此dp[x][0]的实际意义表述为:
dp[x][0]
:环的根节点(不包括)以下的部分,不能选择和 环的根节点 直接相连的两个点的情况下,最多能选择的相互独立的点数。
相应的,dp[x][1]要对dp[u][0]产生贡献,此时u不选择,那么对于我们环上的选取方式没有任何限制。
因此dp[x][1]实际意义表述为:
dp[x][1]
:环的根节点(不包括)以下的部分,最多能选择的相互独立的点数。
由dp[x][0/1]的定义可以看出,dp[x][0/1]的值可以通过两次线性DP扫描环来求得。
Code:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+100;
vector<int> E1[maxn],ET[maxn];
int m,n,N;
int len[maxn],dfn[maxn],dfs_clock;
bool inCircle[maxn];
int fa[maxn];
int dp[maxn][2];
int dp2[maxn][2];
inline void addEdge1(int x,int y){
E1[x].push_back(y);
}
inline void addEdgeT(int x,int y){
ET[x].push_back(y);
}
void input(){
cin>>n>>m;
N =n;
for (int i=0;i<m;i++){
int u,v;
cin>>u>>v;
addEdge1(u,v);
addEdge1(v,u);
}
}
void tarjan(int u){
dfn[u] = ++dfs_clock;
for (int i=0;i<E1[u].size();i++){
int v = E1[u][i];
if (v==fa[u])continue;
if (!dfn[v]){
fa[v] = u;
tarjan(v);
}else if (dfn[v]<dfn[u]){
n++;
len[n] = dfn[u]-dfn[v]+1;
fa[n] = v;
addEdgeT(v,n);
int temp = u;
while (temp!=v){
inCircle[temp] = true;
addEdgeT(n,temp);
temp = fa[temp];
}
}
}
if (!inCircle[u]){
addEdgeT(fa[u],u);
}
dfs_clock--;
}
void work(int x){
int sz = ET[x].size();
if (sz==2){
int son1 = ET[x][0];
int son2 = ET[x][1];
dp[x][0] = dp[son1][0]+dp[son2][0];
dp[x][1] = max(dp[son1][0]+dp[son2][0],max(dp[son1][0]+dp[son2][1],dp[son1][1]+dp[son2][0]));
return;
}
dp2[0][0] =dp[ET[x][0]][0];dp2[0][1]=0;
for (int i=1;i<sz;i++){
dp2[i][0] = max(dp2[i-1][0],dp2[i-1][1])+dp[ET[x][i]][0];
dp2[i][1] = dp2[i-1][0]+dp[ET[x][i]][1];
}
dp[x][0] = dp2[sz-1][0];
dp[x][1] = dp2[sz-1][0];
dp2[sz][0]=dp2[sz][1]=0;
for (int i=sz-1;i>=0;i--){
dp2[i][0] = max(dp2[i+1][0],dp2[i+1][1])+dp[ET[x][i]][0];
dp2[i][1] = dp2[i+1][0]+dp[ET[x][i]][1];
}
dp[x][1] = max(dp[x][1],max(dp2[0][0],dp2[0][1]));
}
void dfs(int u){
dp[u][0]=0;
dp[u][1]=1;
if (u>N)dp[u][0]=0;
for (int i=0;i<ET[u].size();i++){
int v = ET[u][i];
dfs(v);
if (u<=N){
dp[u][0]+=max(dp[v][1],dp[v][0]);
dp[u][1]+=dp[v][0];
}
}
if (u>N){
work(u);
}
}
int main(){
input();
tarjan(1);
dfs(1);
cout<<max(dp[1][0],dp[1][1])<<endl;
return 0;
}