集合划分:状压dp

题目描述

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]);
    }
}  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值