蓝桥杯——暴力枚举篇

暴力枚举

本学期学校开设了C++程序竞赛的课程,每周虽然只有一节课,但老师讲的是真的精彩,建模到映射等等思维的飞跃,还有各种excel、word解题大法,让我叹为观止!总结一些例题,希望能对自己以及想要参加蓝桥杯的你提供些许帮助!(例题、解题方法等往后会不断的更新总结!)

蛮力法:尝试每一种可能

【暴力解法】

  • 确定范围
  • 找出条件
  • 明确输出的个数、类型、次序、格式

一般找数的题目都可以用暴力枚举

范围精确好的好——有些条件可直接放进在范围中

例题1、2013蓝桥杯——猜年龄

【题目描述】

美国数学家维纳(N.Wiener)智力早熟,11岁就上了大学。他曾在1935~1936年应邀来中国清华大学讲学。
一次,他参加某个重要会议,年轻的脸孔引人注目。于是有人询问他的年龄,他回答说:
“我年龄的立方是个4位数。我年龄的4次方是个6位数。这10个数字正好包含了从0到9这10个数字,每个都恰好出现1次。”
请你推算一下,他当时到底有多年轻。

解法一:excel

思路:

利用excel中的=POWER()函数分别计算出1~32的立方和四次方,根据条件:这10个数字正好包含了从0到9这10个数字,每个都恰好出现1次即找到18

image

【解法二】

思路:

  • 正常人的年龄一般0到100岁。由题意:年龄的立方是个4位数。年龄的4次方是个6位数——通过计算器可初步确认范围10到32岁之间

  • 用a[10]数组来计数:记录4位数和6位数每一位中数字0~9出现的次数

  • 如果 a[i]大于1则证明重复 。a[i] == 10 则成立

#include<iostream>
#include<cmath>

using namespace std;

void check(int num, int a[])
{
    while(num != 0)
    {
        
        a[num % 10] ++;
        num = num / 10;
    }
}
 
int main()
{   
    int a[10] = {0};
    int num1, num2, count = 0;
    for(int x = 10; x <= 32; x++)
    {
        num1 = x * x * x;
        if(num1 >= 1000 && num1 <= 9999){
            num2 = num1 * x;
            if(num2 >= 100000 && num2 <= 999999)
            {
                check(num1, a);
                check(num2, a);
                for(int i = 0; i < 10; i++)
                {
                  if(a[i] == 1) count++;
                }              
            }
        }
       if(count == 10)
       {
           cout << x;
           break;
       }
    
    }
return 0;
}

【解法三】:STL容器set:数据不能出现重复;例如100个数中无重复——将这一百个数插入s集合中,最终集合的大小s.size()==100即表明没有重复。

#include<iostream>
#include<cmath>
#include<set>

using namespace std;
int main()
{   
    set<int>s;
    int num1, num2;
    for(int x = 10; x <= 32; x++)
    {
        num1 = x*x*x;
        if(num1 >= 1000 && num1 <= 9999){
            num2 = num1 * x;
            if(num2 >= 100000 && num2 <= 999999)
            {
                while(num1 !=0)
                {
                    s.insert(num1 % 10);
                    num1 = num1 / 10;
                }
                
                while(num2 !=0)
                {
                    s.insert(num2 % 10);
                    num2 = num2 / 10;
                }
            }
        }
        
        if(s.size() == 10)
        {
            cout << x;
            break;
        }
    }
return 0;
}

【解法四】:

  • 将4位数和6位数(整数)转为字符串
  • 将两个字符串拼接(10位数)
  • 检查是否重复(set)

注:引入头文件:#include<set>#include<string>#include<sstream>

整数转字符串方法模板:

void i2s(int num, string & str)
{
    stringstream ss;
    ss << num;
    ss >> str;
}

【参考代码】

#include<iostream>
#include<cmath>
#include<set>
#include<string>
#include<sstream>

using namespace std;

void i2s(int num, string & str)
{
    stringstream ss;
    ss << num;
    ss >> str;
}
bool check(string str)
{   
    set<char>s;// 注意:此时set是char类型
    for(int i=0; i < str.length(); i++)
    {
        s.insert(str[i]);
    }
    if(str.length() == 10 && s.size() == 10) return true;
    return false;
}
int main()
{   
    
    int num1, num2;
    for(int x = 10; x <= 32; x++)
    {
        num1 = x*x*x;
        num2 = num1 * x;
        if(num1 >= 1000 && num1 <= 9999 && num2 >= 100000 && num2 <= 999999){
            // 将整数转为字符串
            string str1, str2;
            i2s(num1, str1);
            i2s(num2, str2);
            // 4位和6位 拼接后检查
            if(check(str1 + str2))
            {
                cout << x;
            }
        }
            
    }
     
return 0;
}

例题2、蓝桥杯——古堡算式

【问题描述】

福尔摩斯到某古堡探险,看到门上写着一个奇怪的算式:
ABCDE * ? = EDCBA
他对华生说:“ABCDE应该代表不同的数字,问号也代表某个数字!”
华生:“我猜也是!”
于是,两人沉默了好久,还是没有算出合适的结果来。
请你利用计算机的优势,找到破解的答案。
把 ABCDE 所代表的数字写出来。
答案写在“解答.txt”中,不要写在这里!

解法:

1、枚举五个数:范围0~9

2、条件:ABCDE应该代表不同的数字 (五位数)

3、条件:ABCDE * ? = EDCBA——x*? = y --> y%x == --->得出x(五位数的才对)

#include<iostream>

using namespace std;

int main()
{   int x, y;
    for(int a = 0; a < 10; a++){
        for(int b = 0; b < 10 ; b++){
            if( b != a)
            for(int c = 0; c < 10; c++){
                if(  c!=a && c!=b)
                for(int d = 0; d < 10 ; d++){
                    if( d!=a && d!=b && d!=c)
                   for(int e = 0; e < 10  ; e++) {
                       if( e!=a && e!=b && e!=c && e!=d){
                        x = a*10000 + b*1000 + c*100 + d*10 + e;//ABCDE
                        y = e*10000 + d*1000 + c*100 + b*10 + a;//EDCBA
                        if(y % x == 0)
                        {
                            cout << x << endl;
                            break;
                        }

                      }
                   }
                }
            }
            
        }
    }
    
return 0;
}

输出结果:

2178
4356
21978

例题3、蓝桥杯——低碳生活大奖赛

【问题描述】

某电视台举办了低碳生活大奖赛。题目的计分规则相当奇怪:

每位选手需要回答10个问题(其编号为1到10),越后面越有难度。答对的,当前分数翻倍;答错了则扣掉与题号相同的分数(选手必须回答问题,不回答按错误处理)。

每位选手都有一个起步的分数为10分。

某获胜选手最终得分刚好是100分,如果不让你看比赛过程,你能推断出他(她)哪个题目答对了,哪个题目答错了吗?

如果把答对的记为1,答错的记为0,则10个题目的回答情况可以用仅含有1和0的串来表示。例如:0010110011 就是可能的情况。

你的任务是算出所有可能情况。每个答案占一行。

思路:

  • 一看到答案所给例子想到用二进制数求解
  • 遍历判断二进制数1就当前分数翻倍;0就当前分数减当前题号所对应分数

由于没搞清二进制数与十进制数范围关系,一开始我遍历的是0-255(2的八次方)(8位无符号二进制数对应十进制范围)是一直没有结果的。本题是10题即十位二进制数:0~1024(2的10次方)

解题关键:要判断二进制数的每一位就显得出十进制数对应二进制数的每一位(应用:二进制数的第k位是几?(可以看本人博客位运算笔记))

例如:
	int x = 10;
    for(int i = 9; i >=0; i++)
    {
		cout << ( x >> k & 1);
    }
输出结果:0 0 0 0 0 0 1 0 1 0 

【参考代码】

#include<iostream>
using namespace std;


int main(){


    int a[10]={10,9,8,7,6,5,4,3,2,1}; // 记录错误题目对应的扣分
    int score = 10;
        for(int x = 0 ; x <= 1024; x++) // 遍历所有无符号10位二进制数对应的十进制数
        {
            
            for(int i = 9; i >= 0; i --) // 将十进制数转为0、1二进制数(左到右一位一位判断)
            {

            if((x >> i & 1) == 1) score = 2 * score; 

                
            else if((x >> i & 1) == 0) score -= a[i];

            
            }
            
              if(score == 100)
              { 
                for(int j = 9; j >= 0; j --)
                cout<< (x >> j & 1)<<" ";
                cout << endl;
              }
              
             score = 10; // 因为score定义的是全局变量,因此没判断完一个x就要更新
        }
    return 0;
}

输出结果:

0 0 1 0 1 1 0 0 1 1 
0 1 1 1 0 1 0 0 0 0 
1 0 1 1 0 1 0 0 0 0 

例题4、蓝桥杯——海盗喝酒

【问题描述】

有一群海盗(不多于20人),在船上比拼酒量。过程如下:打开一瓶酒,所有在场的人平分喝下,有几个人倒下了。再打开一瓶酒平分,又有倒下的,再次重复… 直到开了第4瓶酒,坐着的已经所剩无几,海盗船长也在其中。当第4瓶酒平分喝下后,大家都倒下了。
等船长醒来,发现海盗船搁浅了。他在航海日志中写到:“…昨天,我正好喝了一瓶…奉劝大家,开船不喝酒,喝酒别开船…”
请你根据这些信息,推断开始有多少人,每一轮喝下来还剩多少人没倒下。
如果有多个可能的答案,请列出所有答案,每个答案占一行。
格式是:人数,人数,…
例如,有一种可能是:20,5,4,2,0
多个答案排列顺序不重要。

思路:所有人平分,直到开了第4瓶酒,海盗船长也在其中,我正好喝了一瓶

  • 范围:(不多于20人):则第一轮至少为4人——a1:[4,20] a2:[3,a1) [2,a2) [1,a3)(后面三轮每一轮的人数都会比前一轮少,因为喝醉了),当然你直接1~20也可以

  • 条件:所有人平分,直到开了第4瓶酒,海盗船长也在其中,我正好喝了一瓶(1/a1 + 1/a2 + 1/a3 + 1/a4 == 1)

  • 输出结果五个数,其中最后一个为0,即我们要求四个数(四轮每一轮的人数)

注:1/a1 + 1/a2 + 1/a3 + 1/a4 == 1这样使用不是很规范/整除——因此要通分

【参考代码】

#include<iostream>
using namespace std;


int main(){

    for(int a1 = 4; a1 <= 20; a1++){
        for(int a2 = 3; a2 < a1; a2++){
          for(int a3 = 2; a3 < a2; a3++){
              for(int a4 = 1; a4 < a3; a4++){
                  if(a2*a3*a4 + a1*a3*a4 + a1*a2*a4 + a1*a2*a3 == a1*a2*a3*a4)
                  {
                      cout<< a1 <<" "<< a2 <<" " << a3 <<" "<< a4<<" "<< "0" <<endl;
                  }
              }
          }  
        }
    }
    return 0;
}

例题5、四平方和

【问题描述】

四平方和定理,又称为拉格朗日定理:每个正整数都可以表示为至多4个正整数的平方和。

如果把0包括进去,就正好可以表示为4个数的平方和。

比如:5 = 0^2 + 0^2 + 1^2 + 2^2 7 = 1^2 + 1^2 + 1^2 + 2^2 (^符号表示乘方的意思)

对于一个给定的正整数,可能存在多种平方和的表示法。

要求你对4个数排序:0 <= a <= b <= c <= d

并对所有的可能表示法按 a,b,c,d 为联合主键升序排列,最后输出第一个表示法

程序输入为一个正整数N (N<5000000)

要求输出4个非负整数,按从小到大排序,中间用空格分开

【输入形式】例如,输入:

5

则程序应该输出:

0 0 1 2

思路:

直接暴力枚举,但如果直接四重循环0~n很可能会超时,因此必须进行优化:

1)四平方和 = n——必定有:a * a <= n、b * b <= n、c * c <= n、d * d <= n

2)要求你对4个数排序:0 <= a <= b <= c <= d——循环条件:b = a、c=b开始且最后d要>=c

3)减少循环至三重,d = sqrt(n - a * a - b * b - c * c)

【参考代码】

#include<iostream>
#include<cmath>

using namespace std;

int main()
{
	int n;
	cin>> n;
	for(int a = 0; a * a <= n; a++){
		for(int b = a; b * b <= n; b++){
			for(int c = b; c * c <= n; c++){
				int d = sqrt(n - a*a - b*b - c*c);
				if((d >= c) && (a*a + b*b + c*c + d*d == n))
				{
					cout<<a<<" "<<b<<" "<<c<<" "<<d<<endl; 
				}
			}
		}
	}
	
	return 0;
}

例题6、第100002个素数

【问题描述】

素数就是不能再进行等分的整数。比如:7,11。而9不是素数,因为它可以平分为3等份。一般认为最小的素数是2,接着是3,5,... 请问,第100002(十万零二)个素数是多少? 请注意:2 是第一素数,3 是第二个素数,依此类推。

思路:

方法一for暴力枚举,将所得素数存到一个数组中,从下标1开始,输出q[100002]即可

值得注意的是存储的数据可能会很庞大,因此数组要尽量开得很大,而且作为全局变量!

i的范围我找了蛮久的(10000000、10000000/2都能得出答案),而且数组的范围也要很大才行!

#include<iostream>
#include<cmath>
using namespace std;
int q[1000000]={0};
bool check(int n)
{	
    for(int i = 2; i <= sqrt(n); i++)
    {
        if(n % i == 0) return false;
    }
    
    return true;
}
int main()
{   
    int j = 1;
    for(int i = 2 ; i<10000000/2; i++)
    {
        if(check(i))
        {
            q[j++] = i;
            
        }
    }
    
    cout<<q[100002];
    
    return 0;
}

方法2:为了不那么麻烦的找范围,开大范围数组,直接用while循环找答案,找到即结束!

#include<iostream>
#include<cmath>
using namespace std;

bool check(int n)
{
    for(int i = 2; i<= sqrt(n); i++)
    {
        if(n % i == 0) return false;
    }
    
    return true;
}
int main()
{   
	int count = 0;
	long long x = 2;
	while(count != 100002 )
	{
		if(check(x)) count ++;
		x++;
	}
    cout<<x - 1;
    return 0;
}

注:如果文章有任何错误或不足,请各位大佬尽情指出,评论留言留下您宝贵的建议!如果这篇文章对你有些许帮助,希望可爱亲切的您点个赞推荐一手,非常感谢啦

image

posted @ 2021-10-13 11:03  时间最考验人  阅读(1570)  评论(2编辑  收藏  举报