CSP历年复赛题-P1036 [NOIP2002 普及组] 选数

原题链接:https://www.luogu.com.cn/problem/P1036

题意解读:题目即要在n个数中,枚举出所有的子集,当子集中数字个数刚好为k时,求和,判断是否是素数。

解题思路:

方法一:二进制法

通过二进制法,可以枚举一个集合中所有元素“选”或者“不选”的情况,用二进制1表示选该元素,二进制0表示不选。

下面对样例数据进行模拟:

如上图,从上到下依次枚举0~24-1对应的二进制,根据二进制位的0/1来决定对应元素“选”或“不选”,

k=3的即二进制位有3个1的情况用绿色标识,求和后是素数的用红色标识。

因此对于一般的n个数,只需要枚举0~2n-1,判断每个数的二进制中1的个数是否为k,再针对每个二进制位是0或1决定选或不选对应元素。

这里就引出两个重要的二进制操作:

1、统计二进制有多少个1

首先,介绍一个关键的操作:lowbit,可以返回一个数的二进制中最后一个1所表示的整数

例如x=6,即二进制0110,lowbit(6) = 2,实现如下:

int lowbit(int x)
{
    return x & -x;
}

要计算一个数的二进制中有多少个1,只需要每次都减去这个数的lowbit,直到0为止,统计次数:

int count1(int x)
{
    int cnt = 0;
    while(x)
    {
        x -= lowbit(x);
        cnt++;
    }
}

2、二进制的某一位是0还是1

设整数i,要判断第j个(最低位j=0)二进制位是0还是1,只需要判断i >> j & 1是0还是1

100分代码-二进制法:

#include <bits/stdc++.h>
using namespace std;

const int N = 25;

int n, k, ans;
int a[N];

//计算x的二进制中最后一个1所代表的整数,如11000回返回1000,也就是8
int lowbit(int x)
{
    return x & -x;
}

//计算x的二进制中有多少个1
int count1(int x)
{
    int cnt = 0;
    while(x)
    {
        x -= lowbit(x);
        cnt++;
    }
    return cnt;
}

//判断x是否是素数
bool isprime(int x)
{
    if(x < 2) return false;
    for(int i = 2; i * i <= x; i++)
    {
        if(x % i == 0) return false;
    }
    return true;
}

int main()
{
    cin >> n >> k;
    int pow2n = 1;
    for(int i = 0; i < n; i++)
    {
        cin >> a[i];
        pow2n *= 2; // 计算2^n 
    } 

    for(int i = 0; i < pow2n; i++)
    { 
        if(count1(i) == k) //如果整数i的二进制中1的个数==k
        {
            int sum = 0;
            for(int j = 0; j < n; j++) //遍历二进制的每一位
            {
                if(i >> j & 1) sum += a[j]; //i >> j & 1表示将i右移j位再和1求&,即判断i的第j位二进制是否是1,注意j=0表示第一位
            }
            if(isprime(sum)) ans++;
        }
    }

    cout << ans; 

    return 0;
}

方法二:DFS

n个数字,每个数字只有选或者不选两种情况,可以用一个数组保存选择的状态,1表示选,0表示不选,通过DFS来填充此数组即可。

100分代码-DFS:

#include <bits/stdc++.h>
using namespace std;

const int N = 25;

int n, k, ans;
int a[N];

int tmp[N];

//判断x是否是素数
bool isprime(int x)
{
    if(x < 2) return false;
    for(int i = 2; i * i <= x; i++)
    {
        if(x % i == 0) return false;
    }
    return true;
}

void dfs(int level)
{
    if(level > n) // 找到一组选择
    {
        //判断是否有k个1
        int cnt = 0;
        for(int i = 1; i <= n; i++)
        {
            if(tmp[i] == 1) cnt++;
        }

        if(cnt != k) return;

        int sum = 0;
        for(int i = 1; i <= n; i++)
        {
            if(tmp[i] == 1) sum += a[i];
        }
        if(isprime(sum)) ans++;

        return;
    }
    for(int i = 0; i <= 1; i++)
    {
        tmp[level] = i;
        dfs(level + 1);
    }
}

int main()
{
    cin >> n >> k;
    for(int i = 1; i <= n; i++) cin >> a[i];

    dfs(1);
    cout << ans;

    return 0;
}

 

posted @ 2024-05-21 18:41  五月江城  阅读(55)  评论(0编辑  收藏  举报