问题描述
将LANQIAO中的字母重新排列,可以得到不同的单词,如LANQIAO、AAILNOQ等,注意这7个字母都要被用上,单词不一定有具体的英文意义。
请问,总共能排列如多少个不同的单词。
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案:2520
解决思路:其实这就是一道高中时器的选择题,考点是“排列组合”。
如果题目问你:7个不同字母进行排列,问你能组合出多少种不同的序列,你可以很容易得到答案为:7!=7×6×5×4×3×2×1=5040。但是别急!!!本题的答案并不是5040,因为本题给的7个字母并不是完全不同的,因为其中有两个“A”。因此在刚才的计算中,我们将两个字母“A”视为了不同的字母。解决办法很简单,那就是再除以2,即正确答案应该是:7! / 2 = 7×6×5×4×3×2×1 / 2 = 5040 /2 = 2520。
有同学肯定要问,那要是我偏让你输出这2520个具体的排列呢?那没办法了,上代码:
#include
#include
using namespace std;
int main()
{
char chs[]={'A','A','I','L','N','O','Q'};
int sum=0;
cout<<"具体的排列情况如下:"<<endl;
do{
sum++;
for(int i=0;i<7;i++) cout<<chs[i];
cout<<endl;
}while(next_permutation(chs,chs+7));
cout<<"共"<<sum<<"个"<<endl;
return 0;
}
问题描述
在计算机存储中,12.5MB是多少字节?
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案:13107200
解决办法:作为一名程序员,你至少要知道:1MB(兆字节) = 1024KB(千字节),1KB = 1024B(字节)。
因此对于本题而言,可以直接用电脑自带的“计算器”软件计算:12.5×1024×1024=13107200。
问题描述
由1对括号,可以组成一种合法括号序列:()。
由2对括号,可以组成两种合法括号序列:()()、(())。
由4对括号组成的合法括号序列一共有多少种?
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案:14
解决方案一:
4对括号这个数据并不算大,所以可以直接硬算:
深度为1时:()()()();
深度为2时:(())()()、()(())()、()()(())、(()()())、(()())()、()(()())、(())(());
深度为3时:((()))()、()((()))、((())())、(()(()))、((()()));
深度为4时:(((())))。
解决方案二:
从长远的方向来看,指不定下一次省赛就将这道题变成了编程大题,因此我们还是要给出本题的代码实现:
#include
using namespace std;
int sum,n;
void dfs(int left,int right) //用left和right分别表示(和),由于给定的(和)最初是匹配的
{
if(left == n){ //故所有的非法情况都是由)导致的,因此当(都出现了就不会非法
sum++;
return;
}
dfs(left+1,right); //根据贪心思想,合法的括号组合方式必然是以(开始
if(left > right) dfs(left,right+1); //基于前面的结果,此后在满足left>right时得到的括号序列都将是合法的
}
int main()
{
cin>>n;
dfs(0,0);
cout<<sum<<endl;
return 0;
}
如果题目还要求输出具体的合法序列,则程序可添加为如下内容:
#include
#include
using namespace std;
const int N=1000;
char chs[N];
int sum,n;
void dfs(int left,int right)
{
if(left == n){
sum++;
cout<<chs;
for(int i=strlen(chs);i<n*2;i++) cout<<")";
cout<<endl;
return;
}
chs[left+right]='(';
dfs(left+1,right);
chs[left+right]='\0';
if(left > right){
chs[left+right]=')';
dfs(left,right+1);
}
}
int main()
{
cin>>n;
cout<<"总的括号匹配序列有:"<<endl;
dfs(0,0);
cout<<"共"<<sum<<"个"<<endl;
return 0;
}
问题描述
一个包含有2019个结点的无向连通图,最少包含多少条边?
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案:2018
解决办法:一个具有n个节点的无向连通图,其最少含有的边为n-1(其实就是这个图的生成树)。
问题描述
给定三个整数 a, b, c,如果一个整数既不是 a 的整数倍也不是 b 的整数倍还不是 c 的整数倍,则这个数称为反倍数。
请问在 1 至 n 中有多少个反倍数。
输入格式
输入的第一行包含一个整数 n。
第二行包含三个整数 a, b, c,相邻两个数之间用一个空格分隔。
输出格式
输出一行包含一个整数,表示答案。
样例输入
30
2 3 6
样例输出
10
样例说明
以下这些数满足要求:1, 5, 7, 11, 13, 17, 19, 23, 25, 29。
评测用例规模与约定
对于 40% 的评测用例,1 <= n <= 10000。
对于 80% 的评测用例,1 <= n <= 100000。
对于所有评测用例,1 <= n <= 1000000,1 <= a <= n,1 <= b <= n,1 <= c <= n。
分析:由于数据范围并不大,且计算过程仅仅是3次取余操作,那么在极限情况下,也仅仅是执行100 0000×3次取余操作,以及不多于100 0000次的累加操作。所以可以直接采用暴力法,代码如下:
#include
using namespace std;
int count(int n,int a,int b,int c)
{
int ans=0;
for(int i=1;i<=n;i++)
if(i%a!=0 && i%b!=0 && i%c!=0) ans++;
return ans;
}
int main()
{
int n,a,b,c;
cin>>n>>a>>b>>c;
cout<<count(n,a,b,c);
return 0;
}
问题描述
给定一个单词,请使用凯撒密码将这个单词加密。
凯撒密码是一种替换加密的技术,单词中的所有字母都在字母表上向后偏移3位后被替换成密文。即a变为d,b变为e,…,w变为z,x变为a,y变为b,z变为c。
例如,lanqiao会变成odqtldr。
输入格式
输入一行,包含一个单词,单词中只包含小写英文字母。
输出格式
输出一行,表示加密后的密文。
样例输入
lanqiao
样例输出
odqtldr
评测用例规模与约定
对于所有评测用例,单词中的字母个数不超过100。
分析:题目明确了输入是一个单词,且仅含小写字母,那么处理起来就很简单了。我们直接接受一个字符串str,然后遍历该字符串中的每个字符,将每个字符都与字符’a’进行做差(目的是得到当前字符在小写字母表中的位置,如’a’为0,'b’为1,'c’为2,……以此类推),注意此时得到的差值是一个int型。由于凯撒密码的加密方案是往后移3个位置,那么我们就需要将做差得到的这个差值+3。
这里有一点需要注意:+3可能导致这个值大于26(这样在还原的时候,就会得到非小写字母类字符),因此我们在这里还需要对+3后的这个值进行一个取余运算,代码描述如下:
for(int i-0;i<str.length();i++)
int temp = (str[i]-'a'+3)%26;
接下来就是还原工作了。我们可以直接将得到的temp与字符’a’进行求和运算,此时temp就由int型变成了char型(如0+‘a’=‘a’,1+‘a’=‘b’,2+‘a’=‘c’,……以此类推)。这一过程的代码描述如下:
for(int i-0;i<str.length();i++){
int temp = (str[i]-'a'+3)%26;
str[i] = 'a' + temp;
}
求解本题的完整代码如下:
#include
#include
using namespace std;
string Caesar(string str)
{
for(int i=0;i<str.length();i++)
str[i]='a'+(str[i]-'a'+3)%26;
return str;
}
int main()
{
string str;
cin>>str;
cout<<Caesar(str)<<endl;
return 0;
}
问题描述
对于一个 n 行 m 列的表格,我们可以使用螺旋的方式给表格依次填上正整数,我们称填好的表格为一个螺旋矩阵。
例如,一个 4 行 5 列的螺旋矩阵如下:
1 2 3 4 5
14 15 16 17 6
13 20 19 18 7
12 11 10 9 8
输入格式
输入的第一行包含两个整数 n, m,分别表示螺旋矩阵的行数和列数。
第二行包含两个整数 r, c,表示要求的行号和列号。
输出格式
输出一个整数,表示螺旋矩阵中第 r 行第 c 列的元素的值。
样例输入
4 5
2 2
样例输出
15
评测用例规模与约定
对于 30% 的评测用例,2 <= n, m <= 20。
对于 70% 的评测用例,2 <= n, m <= 100。
对于所有评测用例,2 <= n, m <= 1000,1 <= r <= n,1 <= c <= m。
分析:这是一道典型的模拟类题目。我们的求解思路很简单:首先是根据输入的规格求出对应螺旋矩阵,然后再通过输入的待求位置直接得到答案即可。
问题的关键点就在于怎么求出这个螺旋矩阵?直接解释很难说,请同学们直接看代码(附详细解释,走一遍就能懂)。需要注意的是一定要对count==m*n时进行特殊判断,防止由于正方形矩阵的出现而陷入死循环。
#include
#include
using namespace std;
const int N=1005;
int ary[N][N];
void create(int n,int m)
{
int U=1,R=m,D=n,L=1; //分别表示矩阵的上、右、下、左边界限
int i=1,j=1,count=1;
while(count<=m*n)
{
for(;j<R;j++) ary[i][j]=count++; //从左往右填充
for(;i<D;i++) ary[i][j]=count++; //从上往下填充
for(;j>L;j--) ary[i][j]=count++; //从右往左填充
for(;i>U;i--) ary[i][j]=count++; //从下往上填充
U++,R--,D--,L++;
i++,j++;
if(count == m*n) ary[i][j]=count++; //防止当m=n时陷入死循环
}
}
int main()
{
int n,m,r,l;
cin>>n>>m>>l>>r;
create(n,m);
cout<<ary[l][r]<<endl;
return 0;
}
问题描述
如果一个序列的奇数项都比前一项大,偶数项都比前一项小,则称为一个摆动序列。即 a[2i]a[2i]。
小明想知道,长度为 m,每个数都是 1 到 n 之间的正整数的摆动序列一共有多少个。
输入格式
输入一行包含两个整数 m,n。
输出格式
输出一个整数,表示答案。答案可能很大,请输出答案除以10000的余数。
样例输入
3 4
样例输出
14
样例说明
以下是符合要求的摆动序列:
2 1 2
2 1 3
2 1 4
3 1 2
3 1 3
3 1 4
3 2 3
3 2 4
4 1 2
4 1 3
4 1 4
4 2 3
4 2 4
4 3 4
评测用例规模与约定
对于 20% 的评测用例,1 <= n, m <= 5;
对于 50% 的评测用例,1 <= n, m <= 10;
对于 80% 的评测用例,1 <= n, m <= 100;
对于所有评测用例,1 <= n, m <= 1000。
分析:这是一道典型的动态规划题目,和蓝桥杯 校内模拟赛 奇怪的数列以及蓝桥杯 历届试题 波动数列类似,搜索算法只能对[1,10]的范围内的数据起作用,要想得满分必然要用动态规划。
博主对于动态规划一直都在学习中,因此在看到这道题时,我觉得很有必要为其单独写一篇题解(因为够难,所以也够资格为其单独写题解),这是链接【蓝桥杯】省塞模拟赛 摆动序列(动态规划),下面就直接为Ctrl C党贴满分代码了:
#include
using namespace std;
const int N=1005;
const int MOD=10000;
int dp[N][N];
int DP(int m,int n)
{
for(int i=1;i<=n;i++) dp[1][i]=n-i+1; //初始化dp数组
for(int i=2;i<=m;i++){
if(i&1) //如果i为奇数
for(int j=n;j>=1;j--)
dp[i][j]=(dp[i-1][j-1]+dp[i][j+1])%MOD;
else //否则则为偶数
for(int j=1;j<=n;j++)
dp[i][j]=(dp[i-1][j+1]+dp[i][j-1])%MOD;
}
return m&1?dp[m][1]:dp[m][n]; //根据m的奇偶性返回最后的答案
}
int main()
{
int m,n;
cin>>m>>n;
cout<<DP(m,n)<<endl;
return 0;
}
问题描述
2015年,全中国实现了户户通电。作为一名电力建设者,小明正在帮助一带一路上的国家通电。
这一次,小明要帮助 n 个村庄通电,其中 1 号村庄正好可以建立一个发电站,所发的电足够所有村庄使用。
现在,这 n 个村庄之间都没有电线相连,小明主要要做的是架设电线连接这些村庄,使得所有村庄都直接或间接的与发电站相通。
小明测量了所有村庄的位置(坐标)和高度,如果要连接两个村庄,小明需要花费两个村庄之间的坐标距离加上高度差的平方,形式化描述为坐标为 (x_1, y_1) 高度为 h_1 的村庄与坐标为 (x_2, y_2) 高度为 h_2 的村庄之间连接的费用为
sqrt((x_1-x_2)*(x_1-x_2)+(y_1-y_2)*(y_1-y_2))+(h_1-h_2)*(h_1-h_2)。
在上式中 sqrt 表示取括号内的平方根。请注意括号的位置,高度的计算方式与横纵坐标的计算方式不同。
由于经费有限,请帮助小明计算他至少要花费多少费用才能使这 n 个村庄都通电。
输入格式
输入的第一行包含一个整数 n ,表示村庄的数量。
接下来 n 行,每个三个整数 x, y, h,分别表示一个村庄的横、纵坐标和高度,其中第一个村庄可以建立发电站。
输出格式
输出一行,包含一个实数,四舍五入保留 2 位小数,表示答案。
样例输入
4
1 1 3
9 9 7
8 8 6
4 5 4
样例输出
17.41
评测用例规模与约定
对于 30% 的评测用例,1 <= n <= 10;
对于 60% 的评测用例,1 <= n <= 100;
对于所有评测用例,1 <= n <= 1000,0 <= x, y, h <= 10000。
分析:题目这意思已经很明显了——求最小生成树。
最小生成树算法主要有两个:prim算法、kruskal算法。如果对这两个算法都不熟悉,建议先学习下再来刷这副本。殿堂级学习通道:【算法与数据结构】—— 最小生成树(Prim算法、Kruskal算法)
学习完上面的文章后,下面直接给出用两个不同算法求解本题的完整代码(含详细注释):
方法一:Prim算法求解
#include
#include
#include
#include
using namespace std;
const int N=1005;
struct Node{
int x,y,h;
Node(){ }
Node(int a,int b,int c)
{ x=a,y=b,h=c; }
};
struct Edge{
int x,y;
float cost;
Edge(){ }
Edge(int a,int b,float c)
{ x=a,y=b,cost=c; }
};
bool cmp(Edge e1,Edge e2)
{ return e1.cost<e2.cost; }
float map[N][N]; //户与户之间的距离产生的费用(由于所有户均互通,因此可以直接用邻接矩阵)
class Prim{
private:
int n; //村庄数
float cost; //存放所有户都通电的最小代价
Node node[N]; //存放每户的坐标位置
vector<Edge> e; //存放在执行insert后插入的边
bool vis[N]; //标记某户是否已经通电了
void create() //计算map
{
for(int i=1;i<n;i++)
for(int j=i+1;j<=n;j++)
map[i][j]=map[j][i]=sqrt(pow(node[i].x-node[j].x,2)+pow(node[i].y-node[j].y,2))+pow(node[i].h-node[j].h,2);
}
void insert(int point)
{
vis[point]=true;
for(int i=1;i<=n;i++)
if(!vis[i])
e.push_back(Edge(point,i,map[point][i]));
sort(e.begin(),e.end(),cmp);
}
public:
Prim(int num) //num表示总的村庄个数
{
n=num;
int x,y,h;
for(int i=1;i<=n;i++){
cin>>x>>y>>h;
node[i]=Node(x,y,h);
}
create();
}
void run(int start)
{
insert(start);
int selected=1; //seleced表示当前已经连通了多少户
while(selected<n)
{
for(int i=0;i<e.size();i++){
if(!vis[e[i].y]){
cost+=e[i].cost;
insert(e[i].y);
selected++;
break;
}
}
}
}
float getCost()
{ return cost; }
};
int main()
{
int n;cin>>n;
Prim prim(n); //构建prim
prim.run(1); //从第1个节点出发
printf("%.2f\n",prim.getCost()); //注意格式化输出最终的cost
return 0;
}
方法二:Kruskal算法求解
#include
#include
#include
#include
using namespace std;
const int N=1010;
struct Edge{ //用于表示边的信息
int x,y; //x,y分别表示某两户居民
float cost; //cost则表示这之间电线的造价
Edge(){ } //无参构造函数
Edge(int a,int b,float c) //构造函数
{ x=a,y=b,cost=c; }
};
struct Node{
int x,y,h;
Node(){ }
Node(int a,int b,int c)
{ x=a,y=b,h=c; }
};
bool cmp(Edge e1,Edge e2) //用于sort函数排序时的规则
{ return e1.cost<e2.cost; }
class UnionFindSet{ //并查集类,用于连通两个节点以及判断图中的所有节点是否连通
private:
static const int MAX=N;
int pre[MAX]; //pre数组用于存放某个节点的上级
int rank[MAX]; //rank数组用于降低关系树的高度
int start,n; //start和n分别用于指示并查集里节点的起点和终点
public:
void init(int a,int b) //初始化并查集
{
start=a,n=b;
for(int i=start;i<=n;i++){
pre[i]=i;
rank[i]=1;
}
}
int findPre(int x) //寻找某个节点的上级
{
if(pre[x]==x) return x;
return pre[x]=findPre(pre[x]);
}
bool isSame(int x,int y) //判断某两个节点是否连通
{ return findPre(x)==findPre(y); }
bool unite(int x,int y) //判断两个节是否连通,如果未连通则将其连通并返回真,否则返回假
{
x=findPre(x);
y=findPre(y);
if(x==y) return false;
if(rank[x] > rank[y]) pre[y]=x;
else{
if(rank[x]==rank[y]) rank[y]++;
pre[x]=y;
}
return true;
}
bool isOne() //判断整个无向图中的所有节点是否连通
{
int temp=findPre(start);
for(int i=start+1;i<=n;i++)
if(findPre(i)!=temp) return false;
return true;
}
};
class Kruskal{ //Kruskal算法:求解无向连通图中的最小生成树
private:
int n; //n表示住户个数
static const int MAX=N;
float cost; //存放所有户都通电的最小代价
Node node[N]; //存放每户的坐标位置
vector<Edge> e; //存放录入的无向连通图的所有边
UnionFindSet u; //并查集:抽象实现Graphnew,并完成节点间的连接工作以及判断整个图是否连通
void create() //计算所有住户之间的造价
{
for(int i=1;i<n;i++)
for(int j=i+1;j<=n;j++)
e.push_back(Edge(i,j,sqrt(pow(node[i].x-node[j].x,2)+pow(node[i].y-node[j].y,2))+pow(node[i].h-node[j].h,2)));
}
public:
Kruskal(int num) //num表示总的住户个数
{
n=num;
int x,y,h;
for(int i=1;i<=n;i++){
cin>>x>>y>>h;
node[i]=Node(x,y,h);
}
create();
sort(e.begin(),e.end(),cmp);//对录入的边进行排序
u.init(1,n); //初始化并查集u
}
void run() //求解录入的无向连通图的最小生成树
{
for(int i=0;i<e.size();i++){//按序遍历所有边
if(u.unite(e[i].x,e[i].y)) cost+=e[i].cost; //如果当前边能够被接受,则说明这条边属于最小生成树
if(u.isOne()) break; //一旦并查集的连通分支数为1,则说明求出了最小生成树
}
}
float getCost()
{ return cost; }
};
int main()
{
int n;cin>>n;
Kruskal kruskal(n); //构建kruskal
kruskal.run(); //开始计算
printf("%.2f\n",kruskal.getCost()); //注意格式化输出最终的cost
return 0;
}
问题描述
小明和朋友们一起去郊外植树,他们带了一些在自己实验室精心研究出的小树苗。
小明和朋友们一共有 n 个人,他们经过精心挑选,在一块空地上每个人挑选了一个适合植树的位置,总共 n 个。他们准备把自己带的树苗都植下去。
然而,他们遇到了一个困难:有的树苗比较大,而有的位置挨太近,导致两棵树植下去后会撞在一起。
他们将树看成一个圆,圆心在他们找的位置上。如果两棵树对应的圆相交,这两棵树就不适合同时植下(相切不受影响),称为两棵树冲突。
小明和朋友们决定先合计合计,只将其中的一部分树植下去,保证没有互相冲突的树。他们同时希望这些树所能覆盖的面积和(圆面积和)最大。
输入格式
输入的第一行包含一个整数 n ,表示人数,即准备植树的位置数。
接下来 n 行,每行三个整数 x, y, r,表示一棵树在空地上的横、纵坐标和半径。
输出格式
输出一行包含一个整数,表示在不冲突下可以植树的面积和。由于每棵树的面积都是圆周率的整数倍,请输出答案除以圆周率后的值(应当是一个整数)。
样例输入
6
1 1 2
1 4 2
1 7 2
4 1 2
4 4 2
4 7 2
样例输出
12
评测用例规模与约定
对于 30% 的评测用例,1 <= n <= 10;
对于 60% 的评测用例,1 <= n <= 20;
对于所有评测用例,1 <= n <= 30,0 <= x, y <= 1000,1 <= r <= 1000。
分析:仍在研究中……