蓝桥杯算法特训 | C++ | 暴力破解与实用性优先

课程主要内容

    暴力破解与实用性优先

    (1)暴力破解在大赛及企业应用中的重要性

    (2)暴力破解中的实用性原则

    (3)逆向解法

    (4)枚举法


关键词:枚举(情况少的时候)、逆向解法(逆算)、试探观察(试解)


1.年龄谜题:

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

     

扫描二维码关注公众号,回复: 2674201 查看本文章

实用性:猜测年轻数学家年龄,打印年龄范围的立方和四次方(猜测打印10-35之间)


解题:

#include<iostream>
#include<string>
#include<sstream>
using namespace std;

int main()
{
	for(int i=1;i<100;i++)
	{
		int a=i*i*i;
		int b=i*a;
		
		//数字和字符串的转化 
		string res1,res2;
		stringstream ss1,ss2;
		ss1<<a;
		ss2<<b;
		res1=ss1.str();
		res2=ss2.str(); 
		
		if(res1.length()!=4) continue;
		if(res2.length()!=6) continue;
		
		cout<<i<<" = "<<a<<" "<<b<<endl;
	}
	return 0;
 } 

运行后


可以看出为18



2.罗马数字

古罗马帝国开创了辉煌的人类文明,但他们的数字表示法的确有些繁琐,尤其在表示大数的时候,现在看起来简直不能忍受,所以在现代很少使用了。
之所以这样,不是因为发明表示法的人的智力的问题,而是因为一个宗教的原因,当时的宗教禁止在数字中出现0的概念!
罗马数字的表示主要依赖以下几个基本符号:

I --> 1
V --> 5
X --> 10
L --> 50
C --> 100
D --> 500
M --> 1000

这里,我们只介绍一下1000以内的数字的表示法。
单个符号重复多少次,就表示多少倍。最多重复3次。
比如:CCC表示300  XX表示20,但150并不用LLL表示,这个规则仅适用于I X C M。

如果相邻级别的大单位在右,小单位在左,表示大单位中扣除小单位。
比如:IX表示9  IV表示4  XL表示40 
49 = XLIX
    
更多的示例参见下表,你找到规律了吗?    
I = 1 
II = 2
III = 3
IV = 4
V = 5
VI = 6
VII = 7
VIII = 8
IX = 9 
X = 10
XI = 11
XII = 12
XIII = 13
XIV = 14
XV = 15
XVI = 16
XVII = 17
XVIII = 18
XIX = 19
XX = 20
XXI = 21
XXII = 22
XXIX = 29
XXX = 30
XXXIV = 34
XXXV = 35
XXXIX = 39
XL = 40
L = 50
LI = 51
LV = 55
LX = 60
LXV = 65
LXXX = 80
XC = 90
XCIII = 93
XCV = 95
XCVIII = 98
XCIX = 99
C = 100
CC = 200
CCC = 300
CD = 400
D = 500
DC = 600
DCC = 700
DCCC = 800
CM = 900
CMXCIX = 999

本题目的要求是:请编写程序,由用户输入若干个罗马数字串,程序输出对应的十进制表示。

输入格式是:第一行是整数n,表示接下来有n个罗马数字(n<100)。
以后每行一个罗马数字。罗马数字大小不超过999。
要求程序输出n行,就是罗马数字对应的十进制数据。

例如,用户输入:
3
LXXX
XCIII
DCCII

则程序应该输出:
80
93
702

情况有限:

IV:4    IX:90

XL:40    XC:90

CD:400    CM:900


解题:

#include<iostream>
#include<string>
using namespace std;
//罗马数字枚举法 
int RomeNum(string s)
{
	int sum=0;
	for(int i=0;i<s.length();i++)
	{
		if(s[i]=='I') sum += 1;
		if(s[i]=='V') sum += 5;
		if(s[i]=='X') sum += 10;
		if(s[i]=='L') sum += 50;
		if(s[i]=='C') sum += 100;
		if(s[i]=='D') sum += 500;
		if(s[i]=='M') sum += 1000;
	}
	//补偿、修正
	if(s.find("IV")!=string::npos) sum -=2;
	if(s.find("IX")!=string::npos) sum -=2;
	
	if(s.find("XL")!=string::npos) sum -=20;
	if(s.find("XC")!=string::npos) sum -=20;
	
	if(s.find("CD")!=string::npos) sum -=200;
	if(s.find("CM")!=string::npos) sum -=200;
	return sum;
}
int main()
{
	string a;
	cin>>a;
	cout<<RomeNum(a);
	return 0;
}


反向求解(检查):

#include<iostream>
#include<string>
using namespace std;
string numroman(int x)
{
	int a = x / 1000;	//千位
	int b = x % 1000 /100;
	int c = x % 100 / 10;
	int d = x % 10;
	
	string s = "";
	if(a==1) s += "M";
	if(a==2) s += "MM";
	if(a==3) s += "MMM";
	
	if(b==1) s += "C";
	if(b==2) s += "CC";
	if(b==3) s += "CCC";
	if(b==4) s += "CD";	 
	if(b==5) s += "D";
	if(b==6) s += "DC";
	if(b==7) s += "DCC";
	if(b==8) s += "DCCC";
	if(b==9) s += "CM";
	
	if(c==1) s += "X";
	if(c==2) s += "XX";
	if(c==3) s += "XXX";
	if(c==4) s += "XL";
	if(c==5) s += "L";
	if(c==6) s += "LX";
	if(c==7) s += "LXX";
	if(c==8) s += "LXXX";
	if(c==9) s += "XC";
	
	if(d==1) s += "I";
	if(d==2) s += "II";
	if(d==3) s += "III";
	if(d==4) s += "IV";
	if(d==5) s += "V";
	if(d==6) s += "VI";
	if(d==7) s += "VII";
	if(d==8) s += "VIII";
	if(d==9) s += "IX";
	
	return s;
}
int main()
{
	int n;
	cin>>n;
	cout<<numroman(n);
	return 0;
 } 


3.九宫幻方

如题

小明最近在教邻居家的小朋友小学奥数,而最近正好讲述到了三阶幻方这个部分。

三阶幻方指的是将1~9不重复的填入一个3*3的矩阵当中,使得每一行、每一列和每一条对角线的和都是相同的。  三阶幻方又被称作九宫格,在小学奥数里有一句非常有名的口诀: “二四为肩,六八为足,左三右七,戴九履一,五居其中”, 通过这样的一句口诀就能够非常完美的构造出一个九宫格来。

 4  9  2 
3  5  7
 8  1  6 
有意思的是,所有的三阶幻方,都可以通过这样一个九宫格进行若干 镜像和旋转操作 之后得到。 现在小明准备将一个三阶幻(不一定是上图中的那个)中的一些数抹掉,交给邻居家的小朋友来进行还原,并且希望她能够判断出究竟是不是只有一个解。   而你呢,也被小明交付了同样的任务,但是不同的是,你需要写一个程序~ 

输入格式: 输入仅包含单组测试数据。 每组测试数据为一个3*3的矩阵,其中为0的部分表示被小明抹去的部分。 对于100%的数据,满足给出的矩阵至少能还原出一组可行的三阶幻方。 输出格式: 如果仅能还原出一组可行的三阶幻方,则将其输出,否则输出“Too Many”(不包含引号)。 
样例输入

 0  7  2 
0  5  0
 0  3  0
  样例输出
 6  7  2
 1  5  9
 8  3  4


解题:

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

//矩阵 
/*
4 9 2
3 5 7
8 1 6
旋转 
492357816
834159672
618753294
276951438 
垂直镜像 
2 9 4
7 5 3
6 1 8 
294753618
672159834
816357492
438951876 
*/
 
int main()
{
	string a = "072050030";
	string s[]={
		"492357816",
		"834159672",
		"618753294",
		"276951438",
		"294753618",
		"672159834",
		"816357492",
		"438951876",
		} ;
	for(int i=0;i<8;i++)
	{
		cout<<s[i].substr(0,3)<<endl;
		cout<<s[i].substr(3,3)<<endl;
		cout<<s[i].substr(6,3)<<endl<<endl;
	}
	return 0;
}



4.二阶魔方的旋转

魔方可以对它的6个面自由旋转。

我们来操作一个2阶魔方(下所示)
为了描述方便,我们为它建立了坐标系。

各个面的初始状态如下:
x轴正向:绿
x轴反向:蓝
y轴正向:红
y轴反向:橙
z轴正向:白
z轴反向:黄

假设我们规定,只能对该魔方进行3种操作。分别标记为:
x 表示在x轴正向做顺时针旋转
y 表示在y轴正向做顺时针旋转
z 表示在z轴正向做顺时针旋转

基本旋转后的效果下所示。


xyz 则表示顺序执行x,y,z 3个操作

题目的要求是:
从标准输入获得一个串,表示操作序列。
程序输出:距离我们最近的那个小方块的3个面的颜色。
顺序是:x面,y面,z面。

例如:在初始状态,应该输出:
绿红白

初始状态下,如果用户输入:
x
则应该输出:
绿白橙

初始状态下,如果用户输入:
zyx
则应该输出:
红白绿

猜你喜欢

转载自blog.csdn.net/qq_35924276/article/details/79450673