洛谷之选数

洛谷试题之选数

首先分析题目,从n个数里选m个,输出相加为素数的个数。

由数学中的排列组合可知,有\(C^m_n\), 我们需要列举出所有可能性,这时候我们就可以想到用搜索。

但是还有一个问题 比如1 3 7是和是素数,但是还有五种情况,1 7 3;3 7 1,3 1 7,7 1 3,7 3 1

但是我们只需要输出一个就行了,我个人有两种解决办法。

第一种就是利用排列组合知识,假设有要选出m个数,有\(A^m_n\)种方式,即将m的阶乘。

代码如下:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<queue>
#include<stack>
#include<algorithm>
#include<vector>
#include<map>
#define MAXN 0x3f3f3f3f
using namespace std;
int n, m;
int counts =0;
int a[1000];
bool vis[1000];
int jc(int x)//计算阶乘
{
    int h=1;
    for (int i = 1; i <= x;i++)
        h *= i;
    return h;
}
void dfs(int x,int t)//开搜
{
    if(x==m)//如果已经搜了有m个数
    {
        if(t==1||t==0)
        {
            s=1;
}
        int s = 0;
        for (int i = 2; i < sqrt(t);i++)//判断是不是素数(质数)
        {
            if(t%i==0)
            {
                s = 1;
                break;//如果不是,标记s为1
            }
        }
        if(s!=1)
        {
            counts++;
        }
        return;
    }
    else{
        for (int i = 1; i <= n;i++)
        {
            if(!vis[i])//如果vis[i]没有搜过
            {
            vis[i] = 1;//标记vis[i]为1;
            t += a[i];//求和
            dfs(x + 1, t);//搜下一个数
            vis[i] = 0;//回溯
            t -= a[i];
            }

        }
    }
}
int main()
{
    memset(vis, 0, sizeof(vis));
    cin >> n >> m;
    for (int i = 1; i <= n;i++)
    {
        cin >> a[i];
    }
    dfs(0, 0);
    cout << counts/(jc(m));//除jie去重复的数目,即m的阶乘
    return 0;
}

还有一种方法

我们可以思考下出现重复的原因

拿数据 4 3

3 7 12 19来说

首先 我们从3 开始搜

3 7 12

3 7 19

3 12 7

3 12 19

3 19 7

3 19 12

之所以出现重复 是因为每次我们标记的都是一个点,

如果我们标记小于等于某个点的所有点

那么就不会出现重复的情况。

因此,我们数组里的数据必须是从小到大,样例虽然是从小到大的,但是为了保险起见,还是用sort对数组排一下序。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<queue>
#include<stack>
#include<algorithm>
#include<vector>
#include<map>
#define MAXN 0x3f3f3f3f
using namespace std;
int n, m;
int counts =0;
int a[100000];
bool vis[100000];
/*int jc(int x)
{
    int h=1;
    for (int i = 1; i <= x;i++)
        h *= i;
    return h;
}*/
void dfs(int x,int t)
{
    if(x==m)
    {
        if(t==1||t==0)
        {
            s=1
 }
        int s = 0;
        for (int i = 2; i < sqrt(t);i++)
        {
            if(t%i==0)
            {
                s = 1;
                break;
            }
        }
        if(s!=1)
        {
            counts++;
        }
        return;
    }
    else{
        for (int i = 1; i <= n;i++)
        {
            if(!vis[i])
            {
                for (int j = 1; j <= i;j++)
                   {vis[j] = 1;
                   }//标记比i小的所有点 包括i

                t += a[i];
                dfs(x + 1, t);
                for (int j = 1; j <= i;j++)
                   {vis[j] = 0;
                   }//回溯
                t -= a[i];}

        }
    }
}
int main()
{
    memset(vis, 0, sizeof(vis));
    cin >> n >> m;
    for (int i = 1; i <= n;i++)
    {
        cin >> a[i];
    }
    sort(a + 1, a+n);//sort排序 以防万一顺序顺序不是从小到大
    dfs(0, 0);
    cout << counts;
    return 0;
}



经过为神(为神永远滴神)的指点后,我了解到了另一种解决重复的办法

那就是每搜完一次,就标记上次搜的点,然后从标记的点+1开始搜。

代码如下

#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<queue>
#include<stack>
#include<algorithm>
#include<vector>
#include<map>
#include<cmath>
#define MAXN 0x3f3f3f3f
using namespace std;
int n, m;
int counts =0;
int a[100000];
/*int jc(int x)
{
    int h=1;
    for (int i = 1; i <= x;i++)
        h *= i;
    return h;
}*/
void dfs(int x,int t,int start)
{
    if(x==m)
    {
        int s = 0;
        for (int i = 2; i <sqrt(t);i++)
        {
            if(t%i==0)
            {
                s = 1;
                break;
            }
        }
        if(s!=1)
        {
            counts++;
        }
        return;
    }
    else{
        for (int i = start; i <= n;i++)
        {
            dfs(x + 1, t +a[i], i + 1);//这里将t+=a[i]变成t+a[i]写入下一个搜索函数里,可以不用回溯。start是为了标记位置,防止重复/
            
        }
    }
}
int main()
{
    memset(vis, 0, sizeof(vis));
    cin >> n >> m;
    for (int i = 1; i <= n;i++)
    {
        cin >> a[i];
    }
    sort(a + 1, a+n);
    dfs(0, 0,1);
    cout << counts;
    return 0;
}


posted @ 2020-09-06 15:38  故犹  阅读(189)  评论(0编辑  收藏  举报