题解 P1036 【选数】

关于 P1036 【选数】

嗯,新手试炼场的,错了两次,对,我是蒟蒻。

因为这道题对我有帮助,所以,它是好题。

错啦两次,好尬的。

49——17——100;

不费话了,过程函数与递推。

当然要递推:

49分的不说了,从未先编译一下试试。

跟着题目走,判断质数。

来一段辣鸡代码


#include<bits/stdc++.h>

using namespace std;

int n,k;
int x[25];
int ans;
bool judge_prime(int x)
{
    for(register int i;i*i<=x;i++)
    {
        if(x%i==0)
        {
            return false;
        }
    }
    return true;
}
int rule(int choose_left_num,int already_sum,int start,int end)
{
    if(choose_left_num==0)
    {
        return judge_prime(already_sum);
    }
    int sum=0;
    for(register int i=start;i<=end;i++)
    {
        sum+=rule(choose_left_num-1,already_sum+x[i],i+1,end);
    }
    return sum;
}
int main()
{
    scanf("%d%d",&n,&k);
    for(register int i=1;i<=n;i++)
    {
        scanf("%d",&x[i]);
    }
    ans=rule(k,0,0,n-1);
    printf("%d",ans);
    return 0;
}

递推函数:

choose_left_num为剩余的k;

already_sum为前面累加的和;

start和end为全组合(常见)剩下数字的选取范围;

生成全组合,在过程中逐渐把k个数相加,当选取的数个数为0时,直接返回前面的累加和是否为质数即可;

想了好久才懂得

谢谢题解

nonetheless,

however,

whereas,

but,

就你这种辣鸡水平还想递推,一边呆着!

所谓“骗分过样例,暴力出奇迹” 这段话搜狗拼音第一个(^-^)

暴搜啊,想什么哪?

所以:


#include<bits/stdc++.h>

using namespace std;

int n,k;
bool notp[10000010];
int a[25];
void caesar(void)
{
    for(int i=2;i<10000010;i++)
    {
        if(!notp[i])
        {
            for(int j=i+i;j<10000010;j+=i)
            {
                notp[j]=1;
            }
        }
    }
    return;
}
int cae(int x,int sum,int y)
{
    if(x==0)
    {
        if(notp[sum]) return 0;
        else return 1;
    }
    if(n-y<x)return 0;
    int ans=0;
    for(register int i=y+1;i<=n;i++)
    {
        ans+=cae(x-1,sum+a[i],i);
    }
    return ans;
}
int main()
{
    caesar();
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",a+i);
    }
    printf("%d",cae(k,0,0));
    return 0;
}


老老实实地暴搜。

考虑可能需要进行多次判断,即使如此优化仍可能超时;

考虑一个更优的方法:

注意到相加得到的和最大不超过107,可以构造一个质数表;

每次检查n是否为质数只需要查表即可.如何构造一个这样的质数表呢?

由于一个质数的任意整倍数(除其自身外)都不是质数,任意一个整数都可以被表示为某(几)个质数的乘积,也就是说任意的整数都是某(几)个质数的整倍数;

因此只需将任意一个质数的二倍及以上的整倍数标记为"非质数",就可以获得这样的一张质数表了;

是这段:


int n,k;
bool notp[10000010];//我叫质数婊,呸,质数表
int a[25];
void caesar(void)//我要创造质数,额(⊙o⊙)…质数表
{
    //当前的i是质数,因此将其所有二倍及以上的整倍数都设为非质数,千万不要越界
    for(int i=2;i<10000010;i++)
    {
        if(!notp[i])
        {
            for(int j=i+i;j<10000010;j+=i)
            {
                notp[j]=1;
            }
        }
    }
    return;
}

这样一来就更优了.简单胡乱分析一波:

外层循环将重复约107次,若i不是质数则内层将仅进行1次判断,否则在判断后还将进行约(107-i)/i次赋值操作;

实验证明隐藏自己不打算推式子的事实,该函数的循环仅会运行3.95x107次,反复改变MAX的值可以发现运行次数与MAX的一次方成正比,即该方法可以在线性时间内构造质数表.

到目前为止,我们获得了初始化时间复杂度为O(n),查询时间复杂度为O(1)的质数表.下面,我们尝试回到原来的问题,看看我们还缺些什么?

我们需要从n个数中选出k个并计算它们的和.为了暴搜,我们需要寻找一种方法唯一地定义当前状态?

很容易注意到,每次只向后看是不会漏选的;

举例,如当前选到的数中下标最大的是low,那么选下一个数的时候只从下标为[low+1,n]的数中选不会导致缺失情况也不会导致重复依然不证明

因此可以重新定义当前状态:

还须选i个数,当前的和为sum,当前已经看完了下标为k及以前的数.这样就确保了每次不需要检查重复的情况,也不需要管具体选择了哪些数;

只要一一枚举k以后的数并递归调用就可以了,极大的简化了问题.

递归边界也不难得出:

当还须选0个数的时候,已经选够了,判断当前的和是否为质数,如果是返回1,否则返回0;

而非边界的时候则进行枚举,计算所有情况得到的返回值的和并返回,我们的函数返回值即使在当前限定条件下(从下标大于k的数中选i个数,在sum的基础上加上选的数的和结果是质数)的情况数;

显然在主函数中调用cae(k,0,0)得到的返回值即为结果

最后还有一个小小的可行性剪枝:若cal函数中发现n-k<i则返回0

它:


#include<bits/stdc++.h>

using namespace std;

int n,k;
bool notp[10000010];
int a[25];
void caesar(void)
{
    for(int i=2;i<10000010;i++)
    {
        if(!notp[i])
        {
            for(int j=i+i;j<10000010;j+=i)
            {
                notp[j]=1;
            }
        }
    }
    return;
}
int cae(int x,int sum,int y)
{
    if(x==0)
    {
        if(notp[sum]) return 0;
        else return 1;
    }
    if(n-y<x)return 0;
    int ans=0;
    for(register int i=y+1;i<=n;i++)
    {
        ans+=cae(x-1,sum+a[i],i);
    }
    return ans;
}
int main()
{
    caesar();
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",a+i);
    }
    printf("%d",cae(k,0,0));
    return 0;
}


差不多就这样。

谢谢!



人生总有那么一段大片大片空白的时光。你在等待,你在坚忍,你在静默。你在等一场春华秋实,你在等新一轮的春暖花开,你在等从未有过的雷霆万钧。这静默的日子有些长,有些闷,但是我也会等下去。我相信人的青春不止有一次,有时候,时光会给你额外的惊喜。

There is always a big blank space in life. You are waiting, you are persevering, you are silent. You are waiting for a spring and autumn harvest. You are waiting for a new round of spring blossoms. You are waiting for a thunderbolt that has never happened before. The silent days are long and stuffy, but I will wait. I believe that people have more than one youth. Sometimes, time gives you extra surprises.

人生にはいつも大きな空白の時間がある。あなたは待っていて、あなたは我慢して、あなたは静かにしています。あなたは春の美しさを待っていて、あなたは新しい1ラウンドの春の暖かい花が咲いて、あなたはかつてない雷の危機を待っています。この静かな日は少し長くて、少し退屈ですが、私も待っています。私は人の青春が一度だけではないと信じています。時には、時間はあなたに余分なサプライズを与えます。

原文地址:https://www.cnblogs.com/XSZCaesar/p/10549502.html

时间: 2024-10-29 14:51:26

题解 P1036 【选数】的相关文章

P1036选数

P1036选数 链接 P1036 选数 思路 DFS 代码实现 #include<bits/stdc++.h> using namespace std; const int maxn = 25; typedef long long ll; ll a[maxn]; ll n,k; ll ans; bool check(ll x){ for(int i = 2; i*i <= x; i++){ if(x%i==0) return 0; } return 1; } void dfs(int x

深搜--P1036选数

深搜中绝对会用到递归 因此本题也可以使用深搜来做 bool prime(int b) { memset(sz, true, sizeof(sz)); sz[1]=false; for (int i=2;i<=b;i++) { if (sz[i]) { for (int j=2*i;j<=b;j+=i) sz[j]=false; 定义一个dfs函数来解决对数的搜索 step代表执行步数 sum代表已经选好的数据的和 cet代表已经选完的数的个数 接下来进行搜索 另外介绍下非朴素版的寻找质数的方法

洛谷P1036 选数

题目描述 已知 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 (1<=n<=20,

洛谷—— P1036 选数 || Vijos——选数

https://vijos.org/p/1128|| https://www.luogu.org/problem/show?pid=1036#sub 描述 已知 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. 现在,要求你计算出和为素数共有多少种

P1036 选数

题目描述 已知 nn 个整数 x_1,x_2,…,x_nx1?,x2?,…,xn?,以及11个整数kk(k<nk<n).从nn个整数中任选kk个整数相加,可分别得到一系列的和.例如当n=4,k=3n=4,k=3,44个整数分别为3,7,12,193,7,12,19时,可得全部的组合与它们的和为: 3+7+12=223+7+12=22 3+7+19=293+7+19=29 7+12+19=387+12+19=38 3+12+19=343+12+19=34. 现在,要求你计算出和为素数共有多少种.

1008 选数 2002年NOIP全国联赛普及组

1008 选数 2002年NOIP全国联赛普及组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 查看运行结果 题目描述 Description 已知 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. 现在

codevs——1008 选数

1008 选数 2002年NOIP全国联赛普及组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description 已知 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. 现在,要求你计算出

BZOJ 3930: [CQOI2015]选数 递推

3930: [CQOI2015]选数 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=3930 Description 我们知道,从区间[L,H](L和H为整数)中选取N个整数,总共有(H-L+1)^N种方案.小z很好奇这样选出的数的最大公约数的规律,他决定对每种方案选出的N个整数都求一次最大公约数,以便进一步研究.然而他很快发现工作量太大了,于是向你寻求帮助

集合选数最值一类问题

一共有两种类型,我分别介绍. 类型一 先来看一道简单的题目: POJ2442 Sequence 给你\(m\)个序列,每个序列有\(n\)个非负整数,你现在要在每个序列选一个数,这一共有\(n^m\)种方案,一种方案的值定义为所选的数的和,要你输出值最小的\(n\)种方案的和. 数据范围: \(0 \lt m \le 100\), \(0 \lt n \le 2000\). 先考虑\(m = 2\)的情况,一共有\(n ^ 2\)种方案,设两个序列为\(a, b\),假设我们已经把它们排好序了,