2019牛客暑期多校训练营(第五场)E independent set 1状压dp

题目链接: https://ac.nowcoder.com/acm/contest/885/E

题意:

给你一个26个点的简单图,要你求它的所有 2 n 2^n 2n 个子图中最大独立集的和。

做法:

因为只有26个点,所以我们应该能很快想到状压这个思想,0代表这个点不取,1代表这个点取,但是对于某一个状态0111,我们该如何去定义其合法性?即我们最后放入的那个点应该是哪个(如果我们不先定好,可能会出现很多重复的状态)。

这个时候我们就有一个经典的套路了,即用最低的一位1来代表我们最后放入的那个点,这样的话我们就可以把所有的状态都考虑进去。为什么能把所有状态都考虑进去呢,假设我们01110这个状态合法,即点2 3 4之间没有边,最后放入点4还是点3其实并没有影响,最后更新的答案都是同一个01110,因为01110这个状态已经是保证点2 3 4没有连边的终态了.

类似的例题 http://codeforces.com/contest/11/problem/D

代码又是怎么实现的呢,我们得到这个点之后。状态 d p [ i ] dp[i] dp[i] 的答案从没有点x的状态 d p [ i ⨁ 2 x ] dp[i \bigoplus2^x] dp[i2x] 和有点 x x x 的状态 d p [ i ⨁ 2 x − i ⋀ a [ x ] ] + 1 dp[i \bigoplus2^x-i \bigwedge a[x]]+1 dp[i2xia[x]]+1 两个中更新来。

代码

#include
#define rep(i,a,b) for(int i=(int)a;i<=(int)b;i++)
#define per(i,a,b) for(int i=(int)a;i>=(int)b;i--)
using namespace std;
typedef long long ll;
const int maxn=(1<<26);
char dp[maxn+5];
int a[30],n,m,ans;
int main(){
    scanf("%d%d",&n,&m);
    rep(i,1,m) {
        int u,v; scanf("%d%d",&u,&v);
        a[u]|=(1<<v);
        a[v]|=(1<<u);
    }

    for(int i=1;i<(1<<n);i++){
        int va=((-i)&i);
        int x=log2(va);
        dp[i]=max(dp[i^va],char(dp[(i^va)-(i&a[x])]+1));
        ans+=(int)dp[i];
    }
    printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(dp,图论,思维)