首页 > 其他分享 >CSP历年复赛题-P1036 [NOIP2002 普及组] 选数

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

时间:2024-05-21 18:52:59浏览次数:11  
标签:cnt NOIP2002 int 选数 ++ 不选 P1036 二进制 lowbit

原题链接: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;
}

 

标签:cnt,NOIP2002,int,选数,++,不选,P1036,二进制,lowbit
From: https://www.cnblogs.com/jcwy/p/18204741

相关文章

  • P1036 [NOIP2002 普及组] 选数
    传送锚点:https://www.luogu.com.cn/problem/P1036题目描述已知\(n\)个整数\(x_1,x_2,\cdots,x_n\),以及\(1\)个整数\(k\)(\(k<n\))。从\(n\)个整数中任选\(k\)个整数相加,可分别得到一系列的和。例如当\(n=4\),\(k=3\),\(4\)个整数分别为\(3,7,12,19\)时,可得全部的组合......
  • 洛谷 P1031 [NOIP2002 提高组] 均分纸牌 题解
    题目简述有$N$堆纸牌,编号分别为$1,2,\ldots,N$。每堆上有若干张,但纸牌总数必为$N$的倍数。可以在任一堆上取若干张纸牌,然后移动。移牌规则为:在编号为$1$堆上取的纸牌,只能移到编号为$2$的堆上;在编号为$N$的堆上取的纸牌,只能移到编号为$N-1$的堆上;其他堆上取的纸牌,可......
  • 题解:P10365 [PA2024] Kraniki(评分:8.4)
    前言我们一场模拟赛的题,结果原题是新鲜出炉的。小弟不才,感觉这题是做过的题中几乎最复杂的了。既然搞懂了,就来写一发题解吧。(题外话:目前最优解,我的常数真是小小又大大啊)"Upanddown,glowin'round..."Solution1、一个经典的Trick直接模拟每一种情况显然不可取,考虑计算每......
  • pandas 读取csv 数据,筛选数据
    前言Pandas是一个开源的数据分析和数据处理库,它是基于Python编程语言的。Pandas提供了易于使用的数据结构和数据分析工具,特别适用于处理结构化数据,如表格型数据(类似于Excel表格)。Pandas主要引入了两种新的数据结构:DataFrame和Series。环境准备先pip安装pandas:pi......
  • 动态规划和层次遍历 —— [NOIP2002 普及组] 过河卒
    题目如下:[NOIP2002普及组]过河卒题目描述棋盘上AAA点有一个过河卒,需要走到目标BB......
  • P1002 [NOIP2002 普及组] 过河卒
    题意:卒子过河,有个马,问安全到达终点的路径有多少条。起点在0,0。每一步可以往右或者往下。思路:处理出马的看守点,然后暴力。。看了一下暴力会TLE。400^2.直接dp转移即可。总结:不知道这个还要开longlong,哎。!voidsolve(){pair<int,int>destination;vector<pair<int......
  • P1002 [NOIP2002 普及组] 过河卒
    题目链接:从起点走到终点,最后一步一定是向右或向下走过来的,因此就可以列出状态转移方程。值得注意的是,对于横着和竖着的两条边界不可直接想当然地认为路径数一定等于\(1\),因为在中途可能会有控制点的存在,因此还是要老老实实地列出状态转移方程。由于边界时只会从一个方向递推过来......
  • P1002 [NOIP2002 普及组] 过河卒
    emmmm...据说是比较简单的dp?(再一次意识到自己的菜)链接:https://www.luogu.com.cn/problem/P1002题目:总的思路就是一个状态转移方程吧:dp[x][y]=dp[x-1][y]+dp[x][y-1];然后如果发现这个点不能通过,那么强制修改dp[x][y]=0;代码:#include<iostream>#include<vector>#inc......
  • 【洛谷P1036】 [NOIP2002 普及组] 选数
    一、题目:二、解题思路:本文章采用的解决方法是递归与DFS(深度优先搜索)。以下图是思路图:1.首先-确定位置题目说4个数字取三个数,所以考虑的只有三个位置和这三个位置分别放什么数值。从第一个位置开始放数。2.其次-开始放数分为4种可能,第一位置可以先放3,那么第二个位置......
  • 蓝桥杯 2022 省A 选数异或
    一种比较无脑暴力点的方法,时间复杂度是(n²+m)。(注意==的优先级比^高,记得加括号(a[i]^a[j])==x)#include<iostream>#include<vector>#include<bits/stdc++.h>//包含一些C++标准库中未包含的特定实现的函数的头文件usingnamespacestd;intmain(){intn,......