BZOJ 4316 小C的独立集:圆方树+DP

题意:给定一棵仙人掌图,求其最大独立集。

独立集:点集内任意两点不存在边直接相连。
点相互独立:这些点组成的点集为独立集

题解:

树上情况:dp[i][0/1]表示考虑i为根的子数,i不选/选最多能选几个点。设j为i的儿子,有转移方程:

d p [ i ] [ 0 ] = m a x ( d p [ j ] [ 0 ] , d p [ j ] [ 1 ] )

d p [ i ] [ 1 ] = 1 + d p [ j ] [ 0 ]

A n s = m a x ( d p [ r o o t ] [ 0 ] , d p [ r o o t ] [ 1 ] )

非退化情况:求出圆方树, 圆点使用上面的方法进行转移。对于一个 方点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;
}

猜你喜欢

转载自blog.csdn.net/calabash_boy/article/details/79997056
今日推荐