枚举子集
二进制枚举子集下面代码就是枚举的s的子集(二进制状态压缩)
for(int i=s;i;i=(i-1)&s){//i表示的就是s的子集}
枚举所有子集的子集的时间复杂度
比如一个有n个元素构成的集合,子集的数量是2n2^n2n,现要求枚举所有子集的子集。
一个有k个元素构成的集合,子集的数量是2k2^k2k
考虑nnn个元素构成的集合子集:
元素个数是000的集合个数是Cn0C_n^0Cn0
元素个数是111的集合个数是Cn1C_n^1Cn1
…\dots…
于是有以下等式
Cn0×20+Cn1×2n+⋯+Cnn×2n=(1+2)n=3nC_n^0×2^0+C_n^1×2^n+\dots+C_n^n×2^n=(1+2)^n=3^nCn0×20+Cn1×2n+⋯+Cnn×2n=(1+2)n=3n
由此最终需要枚举3n3^n3n个状态,时间复杂度为Θ(3n)\Theta(3^n)Θ(3n)
Close Group
首先暴力预处理出所有满足题意的连通块,连通块中的点两两之间有直接边。Θ(n2+2n)\Theta(n^2+2^n)Θ(n2+2n)
状态压缩dp
状态表式:fif_ifi表示选择iii这些点构成的最少数量的团
状态计算:枚举iii状态的子集jjj,于是有fi=min(fi,fj+fi⊕j)f_i=min(f_i,f_j+f_{i\oplus j})fi=min(fi,fj+fi⊕j)
时间复杂度:枚举所有状态的子集即上述证明Θ(3n)\Theta(3^n)Θ(3n)
时间复杂度Θ(n2+2n+3n)\Theta(n^2+2^n+3^n)Θ(n2+2n+3n)
318=3874204893^{18}=387 420 489318=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的子集一定满足iorj≤Ki\ or\ j\leq Ki or j≤K
枚举子集,记录子集的最大值和次大值,相加即可
#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;
}