zjhu1029集合划分(状压dp)。

//实质:组合划分 

线性筛:大数据时候用

数据范围小的化,用s筛就够了 

#include<bits/stdc++.h>//状压dp 
using namespace std;
const int N=(1<<15)+5;
int t,n,a[17],p[1700]={1,1},c[N];//17*100即n*ai
int dp[N];//二进制N代表了哪个几个数 ,dp存最大方案数 
int sum[N]={0};//存选中的这几个数的和 
int main(){
	int s,S; 
	for(int i=0;i<15;i++) c[1<<i]=i+1;//显示该权值是第几位 。
	//注意:不能=15会越界访问,且从0开始取 否则c[1]=1会漏则sum[1]错 导致后续sum出错 
	for(int i=2;i*i<1700;i++)//小数据s筛,大数据线行筛
		for(int j=i<<1;j<1700;j+=i) p[j]=1;//把i的各个倍数筛减掉 
			 
	cin>>t;
	while(t--){
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
			scanf("%d",&a[i]);
		S=(1<<n)-1;//就是n个1 
		for(int s=1;s<=S;s++){
			int ls=-s&s,ps=s^ls;//ls是s中最低位1,ps是ls在s中的补集
			if(ls==s) sum[s]=a[c[s]];//只选一个数  
			else sum[s]=sum[ls]+sum[ps];
			dp[s]=(p[sum[s]]==0);//不划分
			//ps&(j-1)  实现每一种划分情况都遍历到  ,因为按位与都为1才输出 
			for(int j=ps;j>0;j=ps&(j-1)) //j和j的补集都是质数 才能给dp[s]累加 
				dp[s]+=dp[j]*(p[sum[j^s]]==0);
		}
		printf("%d\n",dp[S]);
	}
}  

猜你喜欢

转载自blog.csdn.net/weixin_50904510/article/details/120099224