• 洛谷之选数


    洛谷试题之选数

    首先分析题目,从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;
    }
    
    
    
  • 相关阅读:
    对Request.Url片段解析
    Php学习之路四
    解析bmp图像(某年全国软件大赛题目)
    工信部软件大赛(解析bmp)
    Php学习之路三(字符串操作)
    C++二维数组做形参
    php学习之路五(表单验证)
    PHP(学习之路一)
    PHp学习之路二(数组练习)
    解析网页(KMP算法实现部分)
  • 原文地址:https://www.cnblogs.com/a821403286/p/13621915.html
Copyright © 2020-2023  润新知