其他分享
首页 > 其他分享> > 状压DP之集合选数

状压DP之集合选数

作者:互联网

题目

[HNOI2012]集合选数
《集合论与图论》这门课程有一道作业题,要求同学们求出{1, 2, 3, 4, 5}的所有满足以 下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中。同学们不喜欢这种具有枚举性 质的题目,于是把它变成了以下问题:对于任意一个正整数 n≤100000,如何求出{1, 2,..., n} 的满足上述约束条件的子集的个数(只需输出对 1,000,000,001 取模的结果),现在这个问题就 交给你了。

输入格式

只有一行,其中有一个正整数 n,30%的数据满足 n≤20。

输出格式

仅包含一个正整数,表示{1, 2,..., n}有多少个满足上述约束条件 的子集。

样例

样例输入

4

样例输出

8

样例解释

有8 个集合满足要求,分别是空集,{1},{1,4},{2},{2,3},{3},{3,4},{4}。

上思路

1 3 9 27
2 6 18 54
4 12 36 108

由图中我们可以看出我们选出其中一个数,他的上下左右i四个数都不能再选,这让我想到了炮兵营地这道题,但是还是有一定的不同,这里是让我们输出方案数,那道题是用来输出最优解的,不提炮兵营地,单讲这道题;

接下来就是代码了

#include<cstdio>
#include<iostream>
#include<cstring>
#define ll long long 
using namespace std;
const int maxn=1e5+10;
const int mod=1e9+1;
ll f[25][1<<20],ans;
bool flag[maxn];
int n,map[25][25];
inline ll get(int x){//处理
	int limit[20];//记录边界
	map[1][1]=x;//记录初始点
	int m=1,o=1;
	for(int i=1;i<=20;i++){
		if(i>1){
			if(map[i-1][1]*2>n)break;//超过n,跳出
			map[i][1]=map[i-1][1]*2;
			flag[map[i][1]]=1;//标记
		}
		m=i;
		for(int j=2;j<=20;j++){
    		      if(map[i][j-1]*3>n) break;//超过n,跳出
       		      o=max(o,j);//求最大列数
       		      map[i][j]=map[i][j-1]*3;
        	      flag[map[i][j]]=1;//标记
    	      }
	}
	int yy=0;
	for(int i=1;i<=m;i++){
		for(int j=1;j<=o;j++){
			if(!map[i][j])break;
			else map[i][j]=0;
			yy=j;
		}
		limit[i]=0;
		for(int j=yy+1;j<=o;j++)
			limit[i]|=(1<<(o-j));
	}
	for(int i=0;i<(1<<o);i++){
		if(!(i&(i<<1)) && !(i&(i>>1)) && !(limit[1]&i)){
			f[1][i]=1;
		}
	}
	for(int i=2;i<=m;i++){
		for(int j=0;j<(1<<o);j++){
			if(!(j&(j<<1)) && !(j&(j>>1)) && !(limit[i]&j)){
				f[i][j]=0;
				for(int k=0;k<(1<<o);k++){
					if(!(k&(k<<1)) && !(k&(k>>1)) && !(limit[i-1]&k) && !(j&k)){
						f[i][j]=(f[i][j]+f[i-1][k])%mod;					
					}
				}
			}
			
		}
	}
	int sum=0;
	for(int i=0;i<(1<<o);i++){
		if(!(i&(i<<1)) && !(i&(i>>1)) && !(i&limit[m])){
			sum=(sum+f[m][i])%mod;
		}
	} 
	return sum;
} 
int main(){
	scanf("%d",&n);
	ans=1;
	for(int i=1;i<=n;i++){
		if(!flag[i]){
			ans=(ans*get(i))%mod;
		}
	}
	printf("%lld\n",ans);
}

标签:map,状态,int,选数,状压,矩阵,limit,&&,DP
来源: https://www.cnblogs.com/soda-ma/p/13197883.html