高维度前缀和

枚举子集

二进制枚举子集下面代码就是枚举的s的子集(二进制状态压缩)

	for(int i=s;i;i=(i-1)&s)
	{
    
    
		//i表示的就是s的子集
	}

枚举所有子集的子集的时间复杂度
比如一个有n个元素构成的集合,子集的数量是 2 n 2^n 2n,现要求枚举所有子集的子集。
一个有k个元素构成的集合,子集的数量是 2 k 2^k 2k

考虑 n n n个元素构成的集合子集:
元素个数是 0 0 0的集合个数是 C n 0 C_n^0 Cn0
元素个数是 1 1 1的集合个数是 C n 1 C_n^1 Cn1
… \dots
于是有以下等式
C n 0 × 2 0 + C n 1 × 2 n + ⋯ + C n n × 2 n = ( 1 + 2 ) n = 3 n C_n^0×2^0+C_n^1×2^n+\dots+C_n^n×2^n=(1+2)^n=3^n Cn0×20+Cn1×2n++Cnn×2n=(1+2)n=3n

由此最终需要枚举 3 n 3^n 3n个状态,时间复杂度为 Θ ( 3 n ) \Theta(3^n) Θ(3n)

Close Group

首先暴力预处理出所有满足题意的连通块,连通块中的点两两之间有直接边。 Θ ( n 2 + 2 n ) \Theta(n^2+2^n) Θ(n2+2n)

状态压缩dp
状态表式: f i f_i fi表示选择 i i i这些点构成的最少数量的团
状态计算:枚举 i i i状态的子集 j j j,于是有 f i = m i n ( f i , f j + f i ⊕ j ) f_i=min(f_i,f_j+f_{i\oplus j}) fi=min(fi,fj+fij)
时间复杂度:枚举所有状态的子集即上述证明 Θ ( 3 n ) \Theta(3^n) Θ(3n)

时间复杂度 Θ ( n 2 + 2 n + 3 n ) \Theta(n^2+2^n+3^n) Θ(n2+2n+3n)

3 18 = 387420489 3^{18}=387 420 489 318=387420489差不多能过,谁让状态压缩就是那么玄学呢

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<iostream>
#include<algorithm>
using namespace std;
const int N=20;
bool ok[1<<N];
int g[N][N];
int dp[1<<N];
int main()
{
    
    
    IO;
    int T=1;
    //cin>>T;
    while(T--)
    {
    
    
        int n,m;
        cin>>n>>m;
        while(m--)
        {
    
    
            int a,b;
            cin>>a>>b;
            --a,--b;
            g[a][b]=g[b][a]=1;
        }
        for(int i=0;i<1<<n;i++)
        {
    
    
            vector<int> t;
            for(int j=0;j<n;j++)
                if(i>>j&1) t.push_back(j);
            ok[i]=1;
            for(int j=0;j<t.size();j++)
                for(int k=j+1;k<t.size();k++)
                    if(!g[t[j]][t[k]]) ok[i]=0;
        }
        for(int i=0;i<1<<n;i++) dp[i]=n+1;
        dp[0]=0;
        for(int i=1;i<1<<n;i++)
        {
    
    
            if(ok[i]) dp[i]=1;
            for(int j=i;j;j=(j-1)&i)
                dp[i]=min(dp[i],dp[j]+dp[j^i]);
        }
        cout<<dp[(1<<n)-1]<<'\n';
    }
    return 0;
}

E - Or Plus Max

对于K的子集一定满足 i   o r   j ≤ K i\ or\ j\leq K i or jK
枚举子集,记录子集的最大值和次大值,相加即可

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<iostream>
#include<algorithm>
using namespace std;
const int N=500010;
int a[N];
int mx[N],f[N];
int main()
{
    
    
    IO;
    int T=1;
    //cin>>T;
    for(int ca=1;ca<=T;ca++)
    {
    
    
        int n;
        cin>>n;
        for(int i=0;i<1<<n;i++)
        {
    
    
            cin>>a[i];
            mx[i]=a[0];
        }
        for(int i=0;i<1<<n;i++)
            for(int j=i;j;j=(j-1)&i)
            {
    
    
                f[i]=max(f[i],a[j]+mx[i]);
                mx[i]=max(mx[i],a[j]);
            }
        for(int i=1;i<1<<n;i++) 
        {
    
    
            f[i]=max(f[i-1],f[i]);
            cout<<f[i]<<'\n';
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Fighting_Peter/article/details/112133724