算法课-暴力搜索

暴力搜索

课堂例题

啤酒和饮料

 1 /*
 2  *  啤酒每罐2.3元,饮料每罐1.9元。
 3  *  小明买了若干啤酒和饮料,一共花了82.3元。
 4     我们还知道他买的啤酒比饮料的数量少,
 5     请你计算他买了几罐啤酒。
 6  */
 7 //百钱百鸡类问题
 8  9 #include<stdio.h>
10 int main()
11 {
12     int beer = 0 ;
13     int drink = 0 ;
14     int Money ;
15     //技巧1:直接穷举啤酒数量.
16     //通过计算剩余的钱能购买多少瓶饮料?满足题目要求的数量上"啤酒<饮料"
17     for( beer = 1 ; ; beer ++ ){
18         //技巧2:小数不好计算把问题中所有的数目乘以十倍
19         
20         //计算剩余的钱
21         Money = 823 - beer * 23 ;
22         
23         //剩余的钱只能购买整数倍的饮料
24         if( Money % 19 == 0 ){
25             drink = Money / 19 ;
26             
27             // "啤酒<饮料"
28             if( beer < drink ){
29                 printf("beer : %d , drink %d\n",beer,drink);
30                 break;
31             }
32         }
33     }
34     return 0;
35 }
啤酒和饮料

 


 

 

勾股定理

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6441

来源:ccpc2018网络赛D题

 

给定a和n,问是否存在b,c

如果存在b,c请输出其中一组答案

如果不存在请输出“-1,-1”

 

【解析】

分类讨论:

n = 1 : b = 1 , c = a + 1

n = 0 或 n >= 3 (费马大定理) :不存在 b = c = -1

n = 2 :

公式推导加构造

 

a 为奇数时:

 

a 为偶数时:

 

 1 #include<cstdio>
 2 using namespace std;
 3 typedef long long ll;
 4  5 int main()
 6 {
 7     int T ;
 8     scanf("%d",&T);
 9     while( T-- ){
10         int n , a ;
11         scanf("%d%d",&n,&a);
12         if( n == 1 ){
13             printf("%d %d\n",1,a+1);
14         }else if( n == 0 || n >= 3 ){
15             printf("-1 -1\n");
16         }else if( a == 1 && n == 2 ){
17             printf("-1 -1\n");
18         }else if( n == 2 ){
19             if( a % 2 == 1 ){
20                 printf("%d %d\n",(a*a-1)/2,(a*a+1)/2);
21             }else{
22                 printf("%d %d\n",(a*a-4)/4,(a*a+4)/4);
23             }
24         }
25     }
26     return 0 ;
27 }
勾股数

 


 

蓝桥杯 颠倒的价牌

【题意】
    {0,1,2,5,6,8,9}可颠倒,其中颠倒后只有 "6" -> "9" ,  "9" -> "6" ,其余数字没有变化。
    
    价牌确保4位数 (即千位 上的数字不能为0)
    例如
    "1958" -> "8561"
    
    有两个价牌进行翻转后,有一个比原来的数少了"200+",另一个数翻转后多了"800+"
    问:然后将两者的 亏损 (”负两百多“) 和 盈利 (“正八百多”) 后的值加起来 刚好等于558
    
    请问针对亏损的价牌:原来的价牌应该是多少?

 

【题解】
    1、通过搜索枚举出每个位置上的数字 ,并确保千位上不能是0
       枚举的过程中记录两个价牌的情况,即 数字进行颠倒后差值变化为-200多,和+800的情况
    2、然后通过分别枚举刚才搜索出来的结果进行相加 判断其累加后的结果是否为558
       然后输出把对应亏损价牌的原价牌。

 

【具体代码】 

 1 #include<stdio.h>
 2  3 const int N = 2e3 ;
 4  5 //p -> price 通过获取下标来填充
 6 int  p[] = { 0 , 1 , 2 , 5 , 6 , 8 , 9  };
 7 int Mp[] = { 0 , 1 , 2 , 5 , 9 , 8 , 6  };
 8  9 // D1 D2 -> difference 差值 ( 相差两百多 , 相差八百多 )
10 // tot1 , tot2 -> 可能存在多组情况,记录个数
11 // 记录D1中,原价牌数值
12 int D1[N] , Price[N] , tot1 = 0 ;
13 int D2[N] , tot2 = 0 ;
14 15 int choose[4] ;
16 void dfs( int step ){
17 18     if( step == 4 ){
19         int A = 0 ;
20         int B = 0 ;
21         for (int i = 0 ; i < 4 ; i ++ ){
22             A = A * 10 +  p[choose[i]];
23         }
24         for (int i = 3; i >=0 ; i -- ){
25             B = B * 10 + Mp[choose[i]];
26         }
27 28         if( -300 < A - B && A - B < -200 ){
29             Price[tot1] = B ;
30             D1[tot1++] = A - B ;
31         }
32         if( 800 < A - B && A - B < 900 ){
33             D2[tot2++] = A - B ;
34         }
35         return ;
36     }
37     // 首尾不能是'0'
38     for( int i = ( step == 0 || step == 3 ) ; i < 7 ; i ++ ){
39         choose[step] = i;
40         dfs( step + 1 );
41     }
42 }
43 int main()
44 {
45     dfs(0);
46 47     for( int i = 0 ; i < tot1 ; i ++ ){
48         for( int j = 0 ; j < tot2 ; j ++ ){
49             if( D1[i] + D2[j] == 558 ){
50                 printf("%d\n",Price[i]);
51             }
52         }
53     }
54     return 0;
55 }
颠倒的价牌

 


 

子集和问题

【题意】
    给定一个集合和一个正整数c,判定是否存在该集合的子集,使其所有元素的和等于给定的正整数c?
 
【题解】
    暴力枚举每个位置是否为子集中的元素。通过{0,1}来表示,
        如果该位置是"1",那么该位置上的元素被选中,则进行累加,
        否则该位置是"0",该位置上的元素没被选中,不做任何处理。

 

【技巧】
    1、如何枚举该位置上是否为0,1的所有情况?
        答:通过移位运算获取所有情况
            1 << n  左移运算
        
        分析对于n个数来数,每个数字都有选和不选两种情况。
        对于所有方案来说:共有 2^n
  
        for( i = 0 ; i < 1 << n ; i ++ )
          
        即可把所有情况给枚举出来,把 (1<<n)  -> (2 ^ n) 
        
        例如:n = 3 
        则枚举 0 ~ (2^3 - 1)     ->  0  ~ 7
        通过二进制的转换后变成 
        000,001,010,011,100,101,110,111
        
    2、如何判断该位置上是否为0或者1。
        答:通过移位运算即可。
            1 << n  右移运算
        
        //枚举n个位置
        for( j = 0 ; j < n ; j++ ){
            //判断第j个位置上是否为1
            if( (i >> j & 1) == 1 ){
            
            }else{
            
            }
        }
        
        比如:判断"6" ->"110" 各位上的{0,1}情况
        1、从右往左第一位 6 >> 0 & 1 = "110" & "001" = "000" = 0
        2、从右往左第二位 6 >> 1 & 1 = " 11" & "001" = "001" = 1
        3、从右往左第三位 6 >> 2 & 1 = "  1" & "001" = "001" = 1

 

 1 #include<stdio.h>
 2 int a[] = { 1 , 2 , 3 , 4 , 5 , 6 };
 3 int main()
 4 {
 5     int n ;
 6     n = 6 ;
 7     //枚举所有情况
 8     for( int i = 1 ; i < 1 << 6 ; i ++ ){
 9         int tmp = 0 ;
10         //累加上 所有转化为二进制后的 '1' 的位置上对应的数
11         for( int j = 0 ; j < 6 ; j ++ ){
12             if( i >> j & 1 ){
13                 tmp += a[j];
14             }
15         }
16         
17         //满足子集和为n时,输出答案
18         if( tmp == n ){
19             for( int j = 0 ; j < 6 ; j ++ )
20                 printf("%2d",i>>j&1);
21             putchar('\n');
22         }
23     }
24     return 0;
25     return 0;
26 }
子集和问题

 

 


 

背包问题

暴力求解方法:

【题解】
    类比子集合问题,枚举所有子集情况,{1,0}
    "1" 代表物品往背包里放
    "0" 代表不操作
    
    每一个子集都是一种方案。
    先判断是否满足背包容量的前提下,进而更新答案

 

 1 #include<stdio.h>
 2  3 int value[] = { 45 , 25 , 25 };
 4 int weight[] = { 16 , 15 , 15 };
 5 int C = 30 ;
 6 int n = 3 ;
 7 int maxValue = 0 ;
 8 //value , weight 分别代表:每个物品的价值,容量。
 9 //C :背包的总容量
10 //n :物品个数
11 //maxVaulue :背包所能获取的最大价值
12 int main()
13 {
14     for (int i = 0 ; i < 1 << n ; i++ ){
15         int tmpValue = 0 , tmpWeight = 0 ;
16         for( int j = 0 ; j < n ; j ++ ){
17             if( i >> j & 1 ){
18                 tmpValue += value[j] ;
19                 tmpWeight += weight[j] ;
20             }
21         }
22         if( tmpWeight <= C ){
23             maxValue = tmpValue > maxValue ? tmpValue : maxValue ;
24         }
25     }
26     printf("maxValue = %d\n",maxValue);
27     return 0 ;
28 }
背包问题

 


 

n皇后问题

暴力求解方法:

【题意】
    n个皇后放在n*n的棋盘上,皇后之间不能相互攻击(即不能在同一行,同一列,同一对角线上)
    请问有多少种方法?

【题解】
    如果类比子集和问题,那么n*n的格子进行01方法,但是如果针对8皇后来数,8*8=64
        2^64 = 18446744073709551616
    计算量很大,无法实现。显而易见,我们通过64个格子进行{0,1}划分显然不可行。
    
    通过对题目的分析发现有冗余的情况,每一列(行)只能放一个皇后。那么在同一行中其余7个位置都是不能放皇后的。那么我们就根据每一列(行)进行放置,每一列(行)中只有8行(列)可以随意放。
    
    问题规模就变成 8(依次枚举8列)^8(每一列共有8种选择)
        8^8 = 2 ^ 24 = 16777216 < 1e7 可以接受
    
    暴力枚举相当于枚举八进制下,八个位置的情况。并统计合法方案的个数

 

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 const int N = 8 ;
 4 int queen[N] ;
 5 //判断皇后之间放法是否合法
 6 bool check(){
 7     for( int i = 0 ; i < N ; i++ ){
 8         for( int j = 0 ; j < i ; j++ ){
 9             if( queen[i] == queen[j] || abs(i-j) == abs(queen[i]-queen[j]) )
10                 return false ;
11         }
12     }
13     return true ;
14 }
15 int main()
16 {
17     //枚举所有情况,枚举8个位置上的 0~7
18     //统计合法的个数,已知八皇后的方案数为92
19     
20     int Count = 0 ;
21     //枚举所有情况
22     for( int i = 0 ; i < 1 << 24 ; i ++ ){
23         int tmp = i ;
24         //八进制下枚举每一个位置
25         for( int j = 0 ; j < 8 ; j++ ){
26             //第j列的皇后放置在tmp%8的位置上
27             queen[j] = tmp % 8 ;
28             tmp /= 8 ;
29         }
30         //判断是否合法
31         if( check() )
32             Count ++ ;
33     }
34     printf("%d\n",Count);
35     return 0;
36 }
37 // 8 ^ 8 = 2 ^(3*8) = 2 ^ 24 = O(16777216) < O(1e7) 一秒内能执行
n皇后问题

 

 


 

低碳生活大奖赛

【题意】
  某电视台举办了低碳生活大奖赛。题目的计分规则相当奇怪:
 (1)每位选手需要回答10个问题(其编号为1到10),越后面越有难度。答对的,当前分数翻倍;答错了则扣掉与题号相同的分数(选手必须回答问题,不回答按错误处理)。
 (2)每位选手都有一个起步的分数为10分。 某获胜选手最终得分刚好是100分,如果不让你看比赛过程,你能推断出他(她)哪个题目答对了,哪个题目答错了吗?
 (3)如果把答对的记为1,答错的记为0,则10个题目的回答情况可以用仅含有1和0的串来表示。例如:0010110011 就是可能的情况。
 (4) 你的任务是算出所有可能情况。每个答案占一行。

 

【题解】
    枚举所有情况即可,2^10 = 1024 (时间上可以接受)
    按照题意依次进行操作,
        "0" : Score - 10
        "1" : Score * 2 

 

 1 #include<stdio.h>
 2  3 int main()
 4 {
 5     int n = 10 ;
 6     int Count = 0 ;
 7     
 8     //枚举所有情况
 9     for( int i = 0 ; i < 1 << 10 ; i++ ){
10         int Score = 10 ;
11         //根据每个位置上0,1进行操作
12         for(int j = 0 ; j < 10 ; j++ ){
13             if( i >> j & 1 ){
14                 Score *= 2 ;
15             }else{
16                 Score -= 10;
17             }
18         }
19         //最后得分为100
20         if( Score == 100 ){
21             //统计方案数
22             Count ++ ;
23             //输出满足题意的一组解
24             for( int j = 0 ; j < 10 ; j++ ){
25                 printf("%-2d",i>>j&1);
26             }
27             putchar('\n');
28         }
29     }
30     printf("Count = %d\n",Count);
31     return 0;
32 33 }
低碳生活大赛

 


 

课后习题

李白饮酒

【题意】:
  话说大诗人李白,一生好饮。一天,他提着酒壶,从家里出来,酒壶中有酒2斗。他边走边唱:
                无事街上走,提壶去打酒。逢店加一倍,遇花喝一斗。
                
  这一路上,他一共遇到店5次,遇到花10次,已知最后一次遇到的是花,他正好把酒喝光了。请你计算李白遇到店和花的次序,可以把遇店记为a,遇花记为b。则babaabbabbabbbb 就是合理的次序。像这样的答案一共有多少呢?请你计算出所有可能方案的个数。
  为了使问题更加有趣,我们假设他遇到店s次,花f次,你的任务是计算此时的方案总数。
 
【题解】
      暴力出所有01情况,通过判断过程合法 以及 酒刚好被喝完统计其合法的方案数。

 

 1 #include<iostream>
 2 using namespace std;
 3 int Shop , Flower , ans , n ;
 4  5 int bit( int x ){
 6     int res = 0 ;
 7     for( int j = 0 ; j < n ; j++ ){
 8         res += ( x >> j & 1 );
 9     }
10     return res ;
11 }
12 int main()
13 {
14     cin >> Shop >> Flower ;
15     n = Shop + Flower ;
16 17     //枚举所有情况
18     for( int i = 0 ; i < 1<<n ; i++ ){
19         int tmp = 2 ;
20         bool f = true ;
21 22         if( bit(i) == Shop ){
23             for( int j = 0 ; j < n ; j++ ){
24                 if( i >> j & 1 ){
25                     tmp = tmp * 2 ;
26                 }else{
27                     tmp -- ;
28 29                     //过程中不合法:路途中酒壶里没有酒
30                     //这样会导致酒壶里没有酒,遇到店乘以2 即( 0 * 2 = 0 ) 
31                     //而题目要求的是:最后遇到花且刚好把酒给喝完.
32                     if( tmp==0 && j!=n-1 ){
33                         f = false ;
34                         break ;
35                     }
36                 }
37             }
38         }
39         //如果过程中没有不合法,同时刚好把酒喝完
40         if( f && tmp == 0 ){
41             ans ++ ;
42         }
43     }
44     cout << ans << endl ;
45     return 0 ;
46 }
李白打酒

 


 

警匪110

【题意】
​
匪警请拨110,即使手机欠费也可拨通!
    为了保障社会秩序,保护人民群众生命财产安全,警察叔叔需要与罪犯斗智斗勇,因而需要经常性地进行体力训练和智力训练!
    某批警察叔叔正在进行智力训练:
    1 2 3 4 5 6 7 8 9 = 110;
    请看上边的算式,为了使等式成立,需要在数字间填入加号或者减号(可以不填,但不能填入其它符号)。之间没有填入符号的数字组合成一个数,例如:12+34+56+7-8+9 就是一种合格的填法;123+4+5+67-89 是另一个可能的答案。
    请你利用计算机的优势,帮助警察叔叔快速找到所有答案。
    每个答案占一行。形如:
12+34+56+7-8+9
123+4+5+67-89

 

【题解】
    这个题目好比三进制下的暴力枚举过程。
    思路非常简单,但是代码需要一定的实现能力才能解决,过程中需要用到
    
    暴搜枚举符号
        利用dfs枚举三种情况,到叶子结点进行判断求值
        
    构造字符串
        0,1,2 -> "" , "+" , "-"三种情况
        因为过程中有""的出现所以构造字符串显得尤为重要
        
    中缀表达式求值
        开两个栈进行存储,一个栈存数字,另一栈存符号。
        遇到运算符 取出 栈顶两个数进行运算。
        把求得结果重新压栈。

 

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 typedef long long ll;
  4 const int N = 30 ;
  5 char str[N] = {"1 2 3 4 5 6 7 8 9"};
  6 char s[N] ;
  7 int a[N] ;
  8   9 //方案数 , 目标数
 10 int ans = 0 ,target;;
 11  12 //数字栈,符号栈 
 13 stack < int > Nums ;
 14 stack < char > ops ;
 15  16  17 //遇到符号时计算一遍
 18 void Calc(){
 19     int tmp = 1 ;
 20     int v = Nums.top() ; Nums.pop();
 21     int u = Nums.top() ; Nums.pop();
 22     char ch = ops.top() ; ops.pop() ;
 23     if( ch == '+' ) tmp = u + v;
 24     else if( ch == '-' ) tmp = u - v ;
 25     Nums.push( tmp ) ;
 26 }
 27  28  29 int Expression(){
 30   
 31     //初始化操作
 32     while( !Nums.empty() ) Nums.pop();
 33     while( !ops.empty() ) ops.pop();
 34  
 35     int res = 0 , len = 0 ;
 36     int Num1 = 0 , Num2 = 0 ;
 37  
 38     //初始化字符串
 39     memset( s , '\0' , sizeof s );
 40  
 41     //构造s = '(12+34+56+7-8+9)'形式
 42     s[len++] = '(';
 43     for( int i = 0 ; i <=16 ; i++ ){
 44         if( i & 1 ){
 45             if( a[i] == 0 ) continue ;
 46             else if( a[i] == 1 ){
 47                 s[len++] = '+';
 48             }else if( a[i] == 2 ){
 49                 s[len++] = '-';
 50             }
 51         }else{
 52             s[len++] = str[i] ;
 53         }
 54     }
 55     s[len++] = ')';
 56  
 57     //利用栈实现中缀表达式的求值运算
 58     for(int i=0 ; i < len ; i++ ){
 59         if( '0' <= s[i] && s[i] <= '9' ){
 60             int tmp = 0 ;
 61             int j = i ;
 62             while( '0' <= s[j] && s[j] <= '9' ){
 63                 tmp = tmp * 10 + s[j] - '0' ;
 64                 j ++ ;
 65             }
 66             Nums.push(tmp);
 67             i = j - 1 ;
 68         }else{
 69             char c = s[i] ;
 70             if( c == '(' ){
 71                 ops.push(c);
 72             }else if( c == '+' || c == '-' ){
 73                 while( ops.top() != '(' ) Calc();
 74                 ops.push( c );
 75             }else if( c == ')' ){
 76                 while ( ops.top() != '(' ) Calc();
 77                 ops.pop();
 78             }
 79             //else puts(" Invaild Operator !" );
 80         }
 81     }
 82  
 83     //printf("%s %d\n",s,len);
 84  
 85     return Num1 = Nums.top() ;
 86 }
 87  88 //dfs进行爆搜
 89 void dfs( int pos ){
 90     if( pos == 8 ){
 91         if( Expression() == target )
 92             ans ++ ;
 93         return ;
 94     }
 95     //三叉树进行遍历
 96     for( int i = 0 ; i < 3 ; i++ ){
 97         a[pos*2+1] = i ;
 98         dfs( pos + 1 );
 99     }
100 }
101 int main()
102 {
103     cin >> target ;
104     dfs( 0 );
105     cout << ans << endl ;
106     return 0 ;
107 }
警匪110问题

 


 

派遣敢死队

【题意】
    G将军有一支训练有素的军队,这个军队除开G将军外,每名士兵都有一个直接上级(可能是其他士兵,也可能是G将军)。
  现在G将军将接受一个特别的任务,需要派遣一部分士兵(至少一个)组成一个敢死队,
  为了增加敢死队队员的独立性,要求如果一名士兵在敢死队中,他的直接上级不能在敢死队中。
  请问,G将军有多少种派出敢死队的方法。注意,G将军也可以作为一个士兵进入敢死队。 输入的第一行包含一个整数n,表示包括G将军在内的军队的人数。军队的士兵从1至n编号,G将军编号为1。
  接下来n
-1个数,分别表示编号为2, 3, ..., n的士兵的直接上级编号,编号i的士兵的直接上级的编号小于i。

 

【题解】
    暴力枚举 n位二进制,枚举每个人是否被选上敢死队
    过程中判断是否合法(自己直属上司 不能和自己同时在队伍中)。
        注意细节:
        1. G将军没有上司
        2. 敢死队至少有一个人

 

 1 #include<iosteram>
 2 using namespace std;
 3 const int N = 22;
 4 int a[N];
 5 //a[i] : 第i个人的上司为 a[i]
 6  7 int main()
 8 {
 9     int n , ans = 0 ;
10     cin >> n ;
11 12     //G将军特殊处理,他没有上司,所以他的上司设置为一个不存在的值方便统一处理
13     a[0] = n+1 ;
14  
15     bool f ;
16     //编号 1~n -> 0 ~ n-1
17     for( int i = 1 ; i < n ; i++ )
18         cin >> a[i] , a[i] -- ;
19 20     //暴力枚举所有情况,注意从1开始
21     for( int i = 1 ; i < 1 << n ; i++ ){
22         f = true ;
23 24         for( int j = 0 ; j < n ; j++ ){
25             //两种合法情况:当前没有选上敢死队 或者 他的直属上司不在队伍中
26             if( !(i >> j & 1) ||
27                ( (i >> j & 1) && !( i >> a[j] & 1 ) ) ){
28                 continue ;
29             }else{
30                 f = false ;
31                 break ;
32             }
33         }
34         if( f ) ans ++ ;
35     }
36     printf("%d\n",ans);
37     return 0;
38 }
派遣敢死队

 


 

处理机调度问题

【题意】
    用两台处理机A和B处理n个作业。设第i个作业交给A处理需要时间ai,交给B处理需要时间bi。
  由于各作业的特点和机器的性能关系,ai和bi之间没有明确的大小关系。
  既不能将一个作业分开由2台机器处理,也没有一台机器能同时处理2个作业。
  设计一个算法,使得这两台机器处理完这n个作业的时间最短。

 

【题解】
    暴力枚举n位二进制的所有情况.
    当枚举第i位时:
        "1" -> A机器工作 A += a[i]
        "0" -> B机器工作 B += b[i]
    两台机器的最长时间是这种情况的工作时间

 

 1 #include<iostream>
 2 using namespace std;
 3 const int N = 30 ;
 4 int a[N] , b[N] ;
 5 int main()
 6 {
 7     int n ;
 8     cin >> n ;
 9     for( int i = 0 ; i < n ; i++ ){
10         cin >> a[i] ;
11     }
12     for( int i = 0 ; i < n ; i++ ){
13         cin >> b[i] ;
14     }
15     int A , B , Ans = 0x3f3f3f3f ;
16     for( int i = 0 ; i < 1 << n ; i++ ){
17         A = B = 0 ;
18 19         for( int j=0 ; j < n ; j++ ){
20             if( i >> j & 1 ){
21                 A += a[j] ;
22             }else{
23                 B += b[j] ;
24             }
25         }
26         //A,B两台机器同时工作,取决定性的时间最长的那台
27         //答案是记录每一种情况中耗时最少的
28         Ans = min( Ans , max(A,B) );
29     }
30     cout << Ans << endl;
31     return 0 ;
32 }
处理机调度问题

 

 


 

组素数

【题意】
  素数就是不能再进行等分的数。比如:235711 等。9 = 3 * 3 说明它可以3等分,因而不是素数。
我们国家在1949年建国。如果只给你 19、4和9 这4个数字卡片,可以随意摆放它们的先后顺序,那么,你能组成多少个4位的素数呢?
比如:19494919 都符合要求。
为了使问题更加有趣,我们输入n个数字,求这n个数字可以组成的数字中的素数。

【题解】
    我个人认为这个题不应该放在暴力专题中,因为这个题目涉及的是两个经典问题的组合
    1、判断一个数是否为素数?
      a.其中可以用线性筛或者用埃氏筛预处理O(1e7),
        放置在一个数组中,is_prime[]进行判断即可
      b.暴力枚举 根号n 内的因子 O(根号n)
    2、不全相异全排列问题
      经典的搜索问题
      
    而我的做法是正儿八经的暴力。。。
    第一步:预处理1e7内哪些是素数,哪些不是
    第二步:用全排列函数实现所有组合情况 6!
    第三步:排序
    第四步:去重复元素
    第五步:输出答案

 1 #include<iostream>
 2 using namespace std;
 3 typedef long long ll;
 4 const int M = 1e3+10 ;
 5 const int N = 1e7;
 6 int a[M],ans[M],n;
 7  
 8 bool is_prime[N] ;//判断 i 是否为素数. 如果是素数则is_prime[i] = 0 
 9 int prime[N] ;
10 int tot ;
11 12 //素数筛
13 void Euler(){
14     is_prime[0] = is_prime[1] = 1 ;
15     for( ll i = 2 ; i < N ; i ++ ){
16         if( is_prime[i] == 0 ){
17             prime[tot++] = i ;
18         }
19         for( ll j = 0 ; j < tot && (long long) i < N / prime[j] ; j++ ){
20             is_prime[ i *prime[j] ] = 1 ;
21             if( i % prime[j] == 0 ) break ;
22         }
23     }
24 }
25 26 int main()
27 { 
28     Euler() ;
29     cin >> n ;
30     for( int i = 0 ; i < n ; i++ )
31         cin >> a[i] ;
32     sort( a , a+n );
33     int cnt = 0 ;
34  
35     //next_permutation ( a , a + n )
36     //全排列函数
37     do{
38         int tmp = 0 ;
39         for( int i = 0 ; i < n ; i++ ){
40             tmp = tmp * 10 + a[i];
41         }
42         if( !is_prime[tmp] ){
43             ans[cnt++] = tmp ;
44         }
45  
46     }while( next_permutation(a,a+n) );
47 48     //sort( ans , ans + cnt ) 快排函数
49     //cnt = unique( ans , ans + cnt ) - ans 去重函数,返回新的个数
50     sort( ans , ans + cnt );
51     cnt = unique( ans , ans + cnt ) - ans ;
52  
53     if( cnt == 0 ){
54         cout << -1 << endl;
55     }else{
56         for( int i = 0 ; i < cnt ; i++ ){
57             cout << ans[i] << endl;
58         }
59     }
60     return 0 ;
61 }
62 /*
63  
64 6
65 3 3 5 7 8 9
66  
67 */
组素数
posted @ 2020-01-02 14:35  Osea  阅读(480)  评论(0编辑  收藏  举报