题目描述
n个人,每个人手上有一个数ai。
将这些人分成若干组,组没有编号,要求每组人手上的数字之和都是质数。
求合法的分组方案数。输入格式
第一行一个正整数T,表示测试数据的组数
每组数据第一行一个正整数n
每组数据第二行n个正整数a1,a2,…,an
1≤T≤5
1≤n≤15
1≤ ai ≤100输出格式
每组数据一行一个整数,即合法的分组方案数
输入样例 复制
1 3 3 2 5
输出样例 复制
3
#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]);
}
}