试题 算法训练 选数(部分组合问题(模板)超简单,学会这个,组合问题基本通用)

文章讨论了如何利用二进制和组合数学原理解决一个编程问题,即在给定n个整数中,找出k个数相加得到素数的组合数。文章提供了一个AC代码示例,展示了如何统计符合条件的组合数。
摘要由CSDN通过智能技术生成

 已知 n 个整数 x1,x2,…,xn,以及一个整数 k(k<n)。从 n 个整数中任选 k 个整数相加,可分别得到一系列的和。例如当 n=4,k=3,4 个整数分别为 3,7,12,19 时,可得全部的组合与它们的和为:
  3+7+12=22 3+7+19=29 7+12+19=38 3+12+19=34。
  现在,要求你计算出和为素数共有多少种。
  例如上例,只有一种的和为素 数:3+7+19=29。

输入格式

  第一行为空格分开的两个数,分别表示n和k
  第二行为空格分开的n个数,分别表示x1,x2,...,xn

输出格式

  一个整数,表示满足条件的种数

样例输入

4 3
3 7 12 19

样例输出

1

数据规模和约定

  1<=n<=20,k<n
  1<=xi<=5000000

在讲解此题之前,我们先来看看全部组合问题的模板(这里如果像<<   >>  & 等位符号还不懂的,建议先去学习一下,不然看不懂下面的解释的

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,k,x,ct=0,d[25];
void jh(int n){
	for(int i=0;i<(1<<n);i++){
		for(int j=0;j<n;j++){  //输出 
			if(i&(1<<j))cout<<d[j]<<" "; 
 //这里假设n=4,那么就是j== 1 10 100 1000  i=0000(空集) i==0001 0010 0011 0100 0101 ...
		}
		cout<<endl;
	} 
}
signed main(){
	cin>>n>>k;
	for(int i=0;i<n;i++)d[i]=i;	
	jh(n);
}

这里采用二进制输出所有组合数,我们可以发现,用二进制的1来配对数据,举个例子

比如n=4,2^4=16,也就是10000,但我们这里减个1的话就是15,15的二进制是1111,那么从0000-1111每次都和0001,0010,0100,1000进行配对,不就是所有组合(也就是集合的个数)了吗,那么下面给出部分的演示

i=0000 每次和 j=0001,0010,0100,1000 进行配对(i&(1<<j)),可以发现,一个都配对不了,也就是集合中的空集

i=0001 每次和 j=0001,0010,0100,1000 进行配对(i&(1<<j)) 可以发现 只有0001可以配对成功,此时j=0,也就是(1<<j)=>1*2^0=>1  这个1就是0001

i=0010 每次和 j=0001,0010,0100,1000 进行配对(i&(1<<j)) 可以发现 只有0010可以配对成功,此时j=1,也就是(1<<j)=>1*2^1=>2  这个2就是0010

..........

i=1011 每次和 j=0001,0010,0100,1000 进行配对(i&(1<<j)) 可以发现,0001 0010 1000三个都可以配对成功  也就是当i=1011的时候,j输出当j=0001 0010 1000的值,这里的组合就是d[0],d[1],d[3]

以此类推,可能有人看到这里,还是一脸懵逼,其实就是,i的二进制中的每个1,代表集合中对应的值,也就是对应的d的下标。

上面的也是模板,是输出全组合的模板代码

那么步入正题,题目要我们输出C(n,k)的部分组合,怎么办?

这里我们首先在来学习一个神奇的操作,k=k&(k-1),这个操作是什么意思呢?就是输出k的二进制中1的个数如果想知道这个原理的,我另外一个文章有讲解,那么知道1的个数后,按照上面的代码思路,如果1的个数等于题目中的k,那不就可以求出C(n,k)的各个值了吗

我们直接看代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,k,x,ct=0,d[25];
bool zs(int n){
	for(int i=2;i<=sqrt(n);i++){
		if(n%i==0)return 0;
	}
	return 1;
}
void jh(int n){
	for(int i=0;i<(1<<n);i++){
		int num=0,kk=i;
		while(kk){
			kk=kk&(kk-1);
			num++;
		}
		if(num==k){
			int sum=0;
			for(int j=0;j<n;j++){
				if(i&(1<<j))sum+=d[j];
			}
			if(zs(sum))ct++;
		} 
	}
}
signed main(){
	cin>>n>>k;
	for(int i=0;i<n;i++)cin>>d[i];	
	jh(n);
	cout<<ct;
}

这个代码是题目的AC代码,把有关题目元素的代码删除后,也就是求部分组合的模板代码,k由num进行统计判断

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值