2016 【第七届蓝桥杯省赛】 C/C++ C组

目录

第一题:报纸页数

第二题:煤球数目

第三题:平方怪圈

第四题:打印方格

第五题:快速排序

第六题:凑算式

第七题:寒假作业

第八题:冰雹数

第九题:卡片换位

第十题:密码脱落


第一题:报纸页数

X星球日报和我们地球的城市早报是一样的,
都是一些单独的纸张叠在一起而已。每张纸印有4版。

比如,某张报纸包含的4页是:5,6,11,12,
可以确定它应该是最上边的第2张报纸。

我们在太空中捡到了一张X星球的报纸,4个页码分别是:
1125,1126,1727,1728

请你计算这份报纸一共多少页(也就是最大页码,并不是用了几张纸哦)?

请填写表示总页数的数字。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

#include <bits/stdc++.h>
using namespace std;

int main()
{
    int m1 = 1125, m2 = 1126, n1 = 1727, n2 = 1728;
    cout << n1 + m1 << endl;

    //system("pause");
    return 0;
}
/*
答案:2852
*/

第二题:煤球数目

有一堆煤球,堆成三角棱锥形。具体:
第一层放1个,
第二层3个(排列成三角形),
第三层6个(排列成三角形),
第四层10个(排列成三角形),
....
如果一共有100层,共有多少个煤球?

请填表示煤球总数目的数字。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

#include <bits/stdc++.h>
using namespace std;

int main()
{
    int k = 0, sum = 0;
    for (int i = 1; i <= 100; i++)
    {
        k += i;
        sum += k;
    }
    cout << sum << endl;
    //system("pause");
    return 0;
}
/*
答案:171700
*/

第三题:平方怪圈

如果把一个正整数的每一位都平方后再求和,得到一个新的正整数。
对新产生的正整数再做同样的处理。

如此一来,你会发现,不管开始取的是什么数字,
最终如果不是落入1,就是落入同一个循环圈。

请写出这个循环圈中最大的那个数字。

请填写该最大数字。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

所有数字落入同一个循环圈,随便找一个数字试一下就好了。

比如:

3 ->9 -> 81 ->65 ->61 ->{\color{Magenta} 37} ->58 ->89 ->145 ->32->20 ->4->16->{\color{Magenta} 37}

落入37的循环,最大值是145.

#include <bits/stdc++.h>
using namespace std;
int sum(int n)
{
    int t, sum = 0;
    while (n)
    {
        t = n % 10;
        sum += t * t;
        n /= 10;
    }
    return sum;
}
int main()
{
    int n = 3, t, ans = 0;
    for (int i = 1; i < 100; i++)
    {
        n = sum(n);
        ans = max(ans, n);
    }
    cout << ans << endl;

    //system("pause");
    return 0;
}
/*
答案:145
*/

第四题:打印方格

小明想在控制台上输出 m x n 个方格。
比如 10x4的,输出的样子是:

以下是小明写的程序,请你分析其流程,填写划线部分缺少的代码。

#include <stdio.h>

//打印m列,n行的方格 
void f(int m, int n)
{
	int row;
	int col;
	
	for(row=0; row<n; row++){
		for(col=0; col<m; col++) printf("+---");
		printf("+\n");
		for(col=0; col<m; col++) printf("|   ");
		printf("|\n");		
	}
	
	printf("+");
	_____________________________;   //填空
	printf("\n");
}

int main()
{
	f(10,4);
	return 0;
}

注意:仅仅填写划线部分缺少的内容,不要添加任何已有内容或说明性文字。

//答案:
for(col=0; col<m; col++) printf("---+");

第五题:快速排序

排序在各种场合经常被用到。
快速排序是十分常用的高效率的算法。

其思想是:先选一个“标尺”,
用它把整个队列过一遍筛子,
以保证:其左边的元素都不大于它,其右边的元素都不小于它。

这样,排序问题就被分割为两个子区间。
再分别对子区间排序就可以了。

下面的代码是一种实现,请分析并填写划线部分缺少的代码。

#include <stdio.h>

void swap(int a[], int i, int j)
{
	int t = a[i];
	a[i] = a[j];
	a[j] = t;
}

int partition(int a[], int p, int r)
{
    int i = p;
    int j = r + 1;
    int x = a[p];
    while(1){
        while(i<r && a[++i]<x);
        while(a[--j]>x);
        if(i>=j) break;
        swap(a,i,j);
    }
	______________________;
    return j;
}

void quicksort(int a[], int p, int r)
{
    if(p<r){
        int q = partition(a,p,r);
        quicksort(a,p,q-1);
        quicksort(a,q+1,r);
    }
}
    
int main()
{
	int i;
	int a[] = {5,13,6,24,2,8,19,27,6,12,1,17};
	int N = 12;
	
	quicksort(a, 0, N-1);
	
	for(i=0; i<N; i++) printf("%d ", a[i]);
	printf("\n");
	
	return 0;
}

注意:只填写缺少的内容,不要书写任何题面已有代码或说明性文字。 

//答案:
swap(a, p, j);

第六题:凑算式


这个算式中A~I代表1~9的数字,不同的字母代表不同的数字。

比如:
6+8/3+952/714 就是一种解法,
5+3/1+972/486 是另一种解法。

这个算式一共有多少种解法?

注意:你提交应该是个整数,不要填写任何多余的内容或说明性文字。

#include <bits/stdc++.h>

using namespace std;
int main()
{
    int count = 0;
    double num[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    do
    {
        if (num[1] + num[2] / num[3] + (num[4] * 100 + num[5] * 10 + num[6]) / (num[7] * 100 + num[8] * 10 + num[9]) == 10)
            count++;
    } while (next_permutation(num + 1, num + 10));
    cout << count << endl;
    //system("pause");
    return 0;
}
/*
答案:
29
*/

第七题:寒假作业

现在小学的数学题目也不是那么好玩的。
看看这个寒假作业:


   
每个方块代表1~13中的某一个数字,但不能重复。
比如:
 6  + 7 = 13
 9  - 8 = 1
 3  * 4 = 12
 10 / 2 = 5

以及: 
 7  + 6 = 13
 9  - 8 = 1
 3  * 4 = 12
 10 / 2 = 5

就算两种解法。(加法,乘法交换律后算不同的方案)
 
你一共找到了多少种方案?


请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。 

 最简单粗暴的就是全排,但是很慢,五分钟左右出结果。

#include <bits/stdc++.h>
#include <windows.h>
using namespace std;
int main()
{
    //DWORD start, end;
    //start = GetTickCount();
    int count = 0;
    int num[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
    do
    {
        int f1 = (num[1] + num[2] == num[3]);
        int f2 = (num[4] - num[5] == num[6]);
        int f3 = (num[7] * num[8] == num[9]);
        int f4 = ((double)num[10] / num[11] == num[12]);
        if (f1 && f2 && f3 && f4)
        {
            /*for (int i = 1; i <= 12; i++)
                cout << num[i] << ' ';
            cout << '\n';*/
            count++;
        }
    } while (next_permutation(num + 1, num + 14));
    cout << count << endl;

    //end = GetTickCount() - start;
    //cout << "运行时间:" << (double)end / 1000 / 60 << "min" << endl;
    //system("pause");
    return 0;
}
/*
答案:64
运行时间:4.61302min 
*/

较好的办法就是dfs+剪枝。

#include <bits/stdc++.h>
using namespace std;
int num[20], book[20], sum = 0;
void dfs(int step)
{
    if (step > 3 && num[1] + num[2] != num[3])
        return;
    if (step > 6 && num[4] - num[5] != num[6])
        return;
    if (step > 9 && num[7] * num[8] != num[9])
        return;
    if (step > 12 && num[12] * num[11] == num[10]) //能进行到这里,就证明以上的条件都满足了
    { 
        sum++;
        return;
    }
    for (int i = 1; i <= 13; i++) //枚举要填的数
    {
        if (book[i] == 0)
        {
            num[step] = i;
            book[i] = 1; //搜过的标记
            dfs(step + 1);
            book[i] = 0; //搜完取消标记
        }
    }
}
int main()
{
    dfs(1);
    cout << sum << endl;
    //system("pause");
    return 0;
}

第八题:冰雹数

任意给定一个正整数N,
如果是偶数,执行: N / 2
如果是奇数,执行: N * 3 + 1

生成的新的数字再执行同样的动作,循环往复。

通过观察发现,这个数字会一会儿上升到很高,
一会儿又降落下来。
就这样起起落落的,但最终必会落到“1”
这有点像小冰雹粒子在冰雹云中翻滚增长的样子。

比如N=9
9,28,14,7,22,11,34,17,52,26,13,40,20,10,5,16,8,4,2,1
可以看到,N=9的时候,这个“小冰雹”最高冲到了52这个高度。

输入格式:
一个正整数N(N<1000000)
输出格式:
一个正整数,表示不大于N的数字,经过冰雹数变换过程中,最高冲到了多少。

例如,输入:
10
程序应该输出:
52

再例如,输入:
100
程序应该输出:
9232

资源约定:
峰值内存消耗 < 256M
CPU消耗  < 1000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include <xxx>, 不能通过工程设置而省略常用头文件。

提交时,注意选择所期望的编译器类型。

 注意maxn的数据类型要用long long,比如当n=704511时,maxn达到了56991483520,远超int的最大范围。

#include <iostream>
#include <algorithm>
#include <set>
using namespace std;
long long  maxn = 1;
void f(long long  x)
{
    while (x != 1)
    {
        x = (x & 1 ? x * 3 + 1 : x / 2);
        maxn = max(maxn, x);
    }
}
int  main()
{
    int  n;
    cin >> n;
    for (int  i = 2; i <= n; i++)
        f(i);
    
    cout << maxn << '\n';
    //system("pause");
    return 0;
}

第九题:卡片换位

你玩过华容道的游戏吗?
这是个类似的,但更简单的游戏。
看下面 3 x 2 的格子

+---+---+---+
| A | * | * |
+---+---+---+
| B |   | * |
+---+---+---+

在其中放5张牌,其中A代表关羽,B代表张飞,* 代表士兵。
还有一个格子是空着的。

你可以把一张牌移动到相邻的空格中去(对角不算相邻)。
游戏的目标是:关羽和张飞交换位置,其它的牌随便在哪里都可以。

输入格式:
输入两行6个字符表示当前的局面

输出格式:
一个整数,表示最少多少步,才能把AB换位(其它牌位置随意)

例如,输入:

* A
**B

程序应该输出:

17

再例如,输入:

A B
***

程序应该输出:

12


资源约定:
峰值内存消耗 < 256M
CPU消耗  < 1000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include <xxx>, 不能通过工程设置而省略常用头文件。

提交时,注意选择所期望的编译器类型。

思路:

如果计算一共多少总走法,一般用dfs;而只需要输出最短路径,适合用bfs。

每次移动,必然是空格的四周 向 空格 移动,可以看做 空格和四周的交换。 

bfs的话,就是当前空格所在位置,向四周搜索,然后把每个新位置的状态压入队列。

直到搜到正确答案,退出搜索。

如果在移动过程中,产生新的状态之前已经走过了,这种情况就不能再放入队列中,不然就会出现死循环。可以用set解决这个问题。

然后就是,储存地图的时候可以用一维数组,这样更方便储存和比较。

#include <iostream>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
using namespace std;

int posA, posB;
string str1, str2, str;
pair<int, int> posO;
struct StateAndStep
{
    string state;       //当前的状态
    int step;           //步数
    pair<int, int> pos; //当前空格的坐标
    StateAndStep(string _state, int _step, pair<int, int> _pos) : state(_state), step(_step), pos(_pos) {}
};

void bfs()
{
    int next[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; //下一步方向
    queue<StateAndStep> q;
    set<string> allState;

    q.push(StateAndStep(str, 0, posO)); //初始化
    allState.insert(str);

    while (!q.empty())
    {
        StateAndStep &front = q.front(); //队头出列
        string state = front.state;
        int step = front.step;
        pair<int, int> pos = front.pos;

        for (int k = 0; k < 4; k++)
        {
            pair<int, int> tpos;
            tpos.first = pos.first + next[k][0]; //下一步的位置
            tpos.second = pos.second + next[k][1];
            if (tpos.first < 0 || tpos.first > 1 || tpos.second < 0 || tpos.second > 2) //判断是否出界
                continue;

            //新建字符串,并将空格和周边进行交换
            string new_state = state;
            swap(new_state[tpos.first * 3 + tpos.second], new_state[pos.first * 3 + pos.second]);

            if (state[posA] == 'B' && state[posB] == 'A') //满足题目要求时退出
            {
                cout << step << endl;
                return;
            }
            // 查重
            if (allState.find(new_state) != allState.end())
                continue;
            //新状态加入队列,且加入到set中避免后续重复生成状态
            allState.insert(new_state);
            q.push(StateAndStep(new_state, step + 1, tpos));
        }
        q.pop();
    }
}
int main()
{

    getline(cin, str1);
    getline(cin, str2);
    str = str1 + str2;

    posA = str.find('A');
    posB = str.find('B');
    int pos = str.find(' ');
    posO = make_pair(pos / 3, pos % 3);

    bfs();
    //system("pause");
    return 0;
}

第十题:密码脱落

X星球的考古学家发现了一批古代留下来的密码。
这些密码是由A、B、C、D 四种植物的种子串成的序列。
仔细分析发现,这些密码串当初应该是前后对称的(也就是我们说的镜像串)。
由于年代久远,其中许多种子脱落了,因而可能会失去镜像的特征。

你的任务是:
给定一个现在看到的密码串,计算一下从当初的状态,它要至少脱落多少个种子,才可能会变成现在的样子。

输入一行,表示现在看到的密码串(长度不大于1000)
要求输出一个正整数,表示至少脱落了多少个种子。

例如,输入:

ABCBA

则程序应该输出:

0

再例如,输入:

ABDCDCBABC

则程序应该输出:

3

资源约定:
峰值内存消耗 < 256M
CPU消耗  < 3000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include <xxx>, 不能通过工程设置而省略常用头文件。

提交时,注意选择所期望的编译器类型。

方法一:

首先输入字符串s,令i = 字符串开端,j = 字符串末尾,

如果  s[\ i\ ] == s[\ j\ ], 那就i++ , j--.

如果  s[\ i\ ]\ != s[\ j\ ] ,两种办法: i 左边补上s[j], j--;  j 右边补上s[i] ,i++;

其实没必要真的去补,比如,i 左边补s[j]时,我们可以假设补上了,只需要让j--,i 不变。

 

这种思路可以用递归实现。

但是这种方法的的时间复杂度是很高的,比如字符串为ABCDEF,那要补成ABCDEFEDCBA

由此可见,最坏的情况下,最深要搜索到第s.size()层,时间复杂度为2 ^\begin{matrix} s.size() \end{matrix}

所以当字符串长度为30的时候,时间复杂度2^\begin{matrix} 30 \end{matrix}  ≈  10 * 10^8,就要差不多10秒才能跑出来。

但是此题字符串长度最长为1000,这种方法就不可取了,但是分而治之的想法是很好的。

经测试,这种方法能得43分。

#include <iostream>
using namespace std;
string s;
int dfs(int left, int right)
{
    while (s[left] == s[right])
    {
        left++;
        right--;
    }
    if (left >= right)
        return 0;
    return min(dfs(left + 1, right) + 1, dfs(left, right - 1) + 1);//返回两条支路中,短的那条
}
int main()
{
    cin >> s;
    cout << dfs(0, s.size() - 1) << endl;
    //system("pause");
    return 0;
}

方法二:

① 把原字符串倒转过来

② 从s1中的A开始, 在s2中 找对应的 A

③ 这时候,如果让这两个A对称,可以把 CB 补到原字符串的前面

④ 然后到s1中的B,在s2中找对应的B

直接就找到了,说明两个B的位置是对称的,继续

⑤接下来是D,

可以发现,在s2中,隔着一个C,才能要找到D。

说明要想两个D对称,要在把C补到 D前面。

这时候s1已经是回文串了,一共补了三个字母。

 

这是用上找下,我们也可以用下找上,

先找,

 

 

然后补, ,因为s2是s1的翻转,为了方便观察,也可以补在s2的前面,是一样的

再找,

再补,,s2已经是回文串了,补了5个字母;

 

当然,也不用一味地用上找下,或者用下找上,也可以交叉着来

比如:

第一步用下找上

下一步可以这样用用上找下:

 s2变成了回文串,用了8个字母。

那怎么样找,才是最优解呢,

不断尝试后,我们发现了规律:

找到s1和s2的最长公共子序列,

配不上的CB和C,我们把它挪上去。

这个就是正确答案了。

s1 和 s2 的 最长公共子序列的长度 lcs = 7 , 然后 字符串的长度 len = 10, len - lcs = 3 就是正确答案。

 

问题就转换成了求最长公共子序列:

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int dp[1005][1005];
int main()
{
    string s1, s2;
    cin >> s1;
    s2 = s1;
    reverse(s2.begin(), s2.end());

    int len = s1.size();
   
    for (int i = 1; i <= len; i++)
    {
        for (int j = 1; j <= len; j++)
        {
            if (s1[i - 1] == s2[j - 1])
                dp[i][j] = dp[i - 1][j - 1] + 1;
            else
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
        }
    }
    cout << len - dp[len][len] << endl;
    //system("pause");
    return 0;
}

 

方法一虽然不能AC,但是在考场上应该是较为容易想到的,

方法二虽然能AC,但是要想到这是一个最长公共子序列的题目,对于一般人而言,,比赛的时候应该是很难想到的吧。。。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值