“蓝桥杯”练习系统——历届试题

1. 核桃的数量(水题)

思路:求两个数的最大公约数可以用欧几里得算法(辗转相除法),即 a与b的最大公约数 等于 b与(a对b取余) 的最大公约数,当b等于0时,a与b的最大公约数是a。两个数的最小公倍数 等于 其乘积除以两个数的最大公约数。

#include <bits/stdc++.h>

using namespace std;

int gcd(int a, int b)
{
	return b == 0 ? a : gcd(b, a % b);
}

int lcm(int a, int b)
{
	return a * b / gcd(a, b);
}

int main()
{
	int a, b, c;
	cin >> a >> b >> c;
	cout << lcm(lcm(a, b), c) << endl;
	return 0;
} 

2. 打印十字图(找规律)

思路:n为内十字外面的圈的数量

容易看出这是一个中心对称图形,所以只需得到1/4图像然后对称输出即可。

对于这1/4图像,可以定义一个(3+n*2)*(3+n*2)的数组,先设置将左上角的4个.(点)和最外层的一圈$,然后将这一圈$不断向右下移动一格并变符号即可(一圈$一圈.一圈$一圈.)。

#include <bits/stdc++.h>

using namespace std;

int main()
{
	int n;
	cin >> n;
	vector<vector<char> >data(3*n+2, vector<char>(3*n+2, ' '));
	data[0][0] = data[0][1] = data[1][0] = data[1][1] = '.';
	data[1][2] = data[2][1] = data[2][2] = '$';
	for (int i = 2; i < 3 + n * 2; ++i)
	{
		data[0][i] = data[i][0] = '$';
	}
	
	for (int i = 1; i < 3 + n * 2; ++i)
	{
		for (int j = 1; j < 3 + n * 2; ++j)
		{
			if (data[i][j] == ' ')
			{
				if (data[i - 1][j - 1] == '$')
					data[i][j] = '.';
				else
					data[i][j] = '$';
			}
		} 
	}
	
	for (int i = 0; i < 3 + n * 2; ++i)
	{
		for (int j = 0; j < 3 + n * 2; ++j)
		{
			cout << data[i][j];
		}
		for (int j = 1 + n * 2; j >= 0; --j)
		{
			cout << data[i][j];
		}
		cout << endl;
	}
	for (int i = 1 + n * 2; i >= 0; --i)
	{
		for (int j = 0; j < 3 + n * 2; ++j)
		{
			cout << data[i][j];
		}
		for (int j = 1 + n * 2; j >= 0; --j)
		{
			cout << data[i][j];
		}
		cout << endl;
	}
	
	return 0;
} 

3. 带分数(搜索)

思路:对问题描述进行分析,可以得出4个条件:

1)num == a + b / c

2)  a < num

3) b >= c

4) b % c == 0

所以,可以使用DFS(深度优先搜索)得到数字1-9的全排列,然后对该集合中的每个项根据上面的4个条件进行剪枝,得出结果并输出。提交之后发现运行超时。。。。。。

#include <bits/stdc++.h>

using namespace std;

int num, tCount;

//对1-9的全排列剪枝 
void filter(int num, string str)
{
	int a, b, c;
	
	//求得num的位数
	char tNum[7];
	itoa(num, tNum, 10);
	int numLen = strlen(tNum);
	
	//a < num
	for (int i = 1; i <= numLen; ++i)
	{
		a = atoi(str.substr(0, i).c_str());
		if (i == numLen && num <= a)
			continue;
		for (int j = ceil((9 - i) / 2); j <= 8 - i; ++j) 
		{
			b = atoi(str.substr(i, j).c_str());
			c = atoi(str.substr(i + j, 9 - i - j).c_str());
			if (b < c)
				continue;
			if (b % c != 0)
				continue;
			if (num != a + b / c)
				continue;
			++tCount;
		}
	} 
}

//生成1-9的全排列
bool flag[10] = {false};
void dfs(bool* tflag = flag, int index = 1, string str = "")
{
	if (index == 10)
		filter(num, str);
	for (int i = 1; i < 10; ++i)
	{
		if (flag[i])
			continue;
		flag[i] = true;
		char t = '0' + i;
		str = str + t;
		dfs(tflag, index + 1, str);
		str = str.substr(0, (int)str.size() - 1);
		flag[i] = false;
	}	
}

int main()
{
	cin >> num;
	dfs();
	cout << tCount << endl;
	return 0;
} 

然后,参考了其他文章的代码稍整理后顺利AC,原文章链接:https://blog.csdn.net/fanke666/article/details/69644216

大体的思路为:由于abc一共9位数字,a至少1位,而b>=c,所以c至多有4为,所有设置两个for循环,外层从1到num-1遍历a,内层从1到10000-1遍历c,而b由num = a + b / c得到。至于前一种方法运行超时的原因,我分析可能的原因是先生成了全排列之后再进行分割判断,而没有在全排列的前几位已经不满足条件时终止生成,欢迎大家在评论中给出建议,哈哈哈哈哈哈。

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

int flag[10];
//统计1-9的数目并检测是否含0
int statistic(int x) {
	do {
		int tmp = x % 10;
		if(tmp == 0) 
		{
			return 0;
		}
		flag[tmp]++;
	} while(x = x / 10);
	return 1;
}

//检测当前时候有重复的1-9
int statisticOk() {
	for(int i = 1; i < 10; i++) 
	{
		if(flag[i] != 1) 
		{
			return 0;
		}
	}
	return 1;
}

int main() 
{
	int num, count = 0, a, b, c;
	cin >> num;
	//从1到num-1遍历a
	for(a = 1; a < num; a++) 
	{
		//每次对flag清0
		memset(flag, 0, sizeof(flag));
		if(!statistic(a))
		{
			continue;
		}
		//从1到10000-1遍历c,由num = a + b / c得到b
		//暂时保存a的数字统计,以便于回退再测试下一个c或b
		int tFlag[10];
		for(c = 1; c < 10000; c++) 
		{
			memcpy(tFlag, flag, sizeof(flag));
			if(!statistic(c))
			{
				memcpy(flag, tFlag, sizeof(flag));
				continue;
			}
			b = (num - a) * c;
			if(!statistic(b))
			{
				memcpy(flag, tFlag, sizeof(flag));
				continue;
			}
			if(!statisticOk())
			{
				memcpy(flag, tFlag, sizeof(flag));
				continue;
			}
			memcpy(flag, tFlag, sizeof(flag));
			count++;
		}
	}
	cout << count << endl;
	return 0;
} 

4. 剪格子(搜索、DFS)

思路:图的深度优先遍历算法的应用,较简单

#include <bits/stdc++.h>

using namespace std;

int m, n, sum = 0;
int data[10][10];
bool flag[10][10];
int dx[4] = {0,1,0,-1};
int dy[4] = {1,0,-1,0};

int dfs(int x, int y, int num)
{
    if(num == sum / 2)
        return 1;
    for(int i = 0;i < 4; ++i)
    {
        int nx = x + dx[i];
        int ny = y + dy[i];
        if(nx < 1 || ny <1 || nx > n || ny > m || flag[nx][ny] || num + data[nx][ny] > sum / 2)
            continue;
        flag[nx][ny] = true;
        int res = dfs(nx, ny, num + data[nx][ny]);
        if(res)
            return res + 1;
        flag[nx][ny] = false;
    }
    return 0;
}

int main()
{
    memset(flag, 0, sizeof(flag));
    cin >> m >> n;
    for(int i = 1; i <= n; ++i)
    {
        for(int j = 1; j <= m; ++j)
        {
            cin >> data[i][j];
            sum += data[i][j];
        }
    }
    //和为奇数肯定不符合要求
    if(sum % 2 == 1)
    {
        cout<<0<<endl;
    }
    else
    {
        flag[1][1] = true;
        cout << dfs(1, 1, data[1][1]) <<endl;
    }
    return 0;
}

5. 错误票据(水题)

思路:对输入的数据进行排序后,遍历检查第2到n-1个元素的值,若与前一个元素相等,则为重号ID,因为可能与值最大的ID重号,所以应该检查2到n个元素;断号ID检查第2到n-1个元素是否与前一个元素的差为2即可。

#include <bits/stdc++.h>

using namespace std;

int main()
{
    int key;
    vector<int>data;
    cin >> key;
    while(cin >> key)
        data.push_back(key);
    sort(data.begin(), data.end());
    int a, b;
    for (int i = 1; i < (int)data.size(); ++i)
    {
        if (data[i] == data[i - 1] + 2)
            a = data[i] - 1;
        if (data[i] == data[i - 1])
            b = data[i];
    }
    cout << a << " " <<  b << endl;
    return 0;
}

6. 翻硬币(贪心)

思路:比较源字符串和目标字符串,找出第一个不同的字符,将这个字符与其后面的字符进行o<-->*翻转;若该字符是串尾,则翻转其与之前的字符,但实际上这种情况是不存在的,因为检查到串尾元素不同,也就意味着之前的其他字符均相同,而每次要翻转相邻的两个字符,所以题目将无法得到解。至于为什么这样翻次数最少我还没用理解,欢迎大家在评论区讨论分享。

#include <bits/stdc++.h>

using namespace std;

int main()
{
    string a, b;
    cin >> a >> b;
    int len = (int)a.size();
    int i, cnt = 0;
    for (i = 0;i < len; ++i)
    {
        if (a[i] != b[i])
        {
            ++cnt;
            if (a[i] == '*')
                a[i] = 'o';
            else
                a[i] = '*';
            if (i == len - 1)
            {
                if (a[i - 1] == '*')
                    a[i - 1] = 'o';
                else
                    a[i - 1] = '*';
            }
            else
            {
                if (a[i + 1] == '*')
                    a[i + 1] = 'o';
                else
                    a[i + 1] = '*';
            }
        }
    }
    cout << cnt << endl;
    return 0;
}

7. 连号区间数(水题)

思路:若[L, R]为连号区间,则其中的最大数max减最小数min==区间内元素数目减1==R减L。所以遍历每个子串并且判断是否符合条件即可,时间复杂度为O(n^2)。

#include <bits/stdc++.h>

using namespace std;

int main()
{
    int num, cnt = 0;
    cin >> num;
    vector<int>data(num);
    for (int i = 0; i < num; ++i)
    {
        cin >> data[i];
    }
    int mmax, mmin;
    for (int i = 0;i < num; ++i)
    {
        mmax = mmin = data[i];
        for (int j = i; j < num; ++j)
        {
            if (data[j] > mmax)
                mmax = data[j];
            if (data[j] < mmin)
                mmin = data[j];
            if (mmax - mmin == j - i)
                ++cnt;
        }
    }
    cout << cnt << endl;
    return 0;
}

8. 买不到的数目(数论)

思路:数论的知识,若自然数a,b互质,则不能表示成ax+by(x, y为非负整数)的最大整数是ab-a-b。当然,题目中没有提到输入的两个数互质,但若a, b不互质,那么不存在最大值,例如2,4,所有4n-1和4n-3(n>0且n为整数)都不能得到组合。

#include <bits/stdc++.h>

using namespace std;

int main()
{
    int a, b;
    cin >> a >> b;
    cout << a * b - a - b << endl;
    return 0;
}

9. 大臣的旅费(Floyd算法/DFS)

方法一:Floyd算法(时间复杂度O(n^3),因运行超时只能得到75分)

思路:首先通过题目描述可以得到,由于节省开支,任意两个城市之间连通且路径唯一,即任意两个城市间的最短路径和最长路径是一条路径。通过Floyd算法求出任意两点间的最短路径,然后找出其中最大的数x,按照题目中的计费规则,费用为10*x+x!。

注意:本人第一次使用Floyd算法进行问题求解,编程过程中遇到的问题在下面一一列出

1)若路径长度的存储数据类型为int,那么容易想到用一个很大的数表示两点之间不可达,但如果使用INT_MAX(系统定义的int类型最大值)就会出现一个问题,即INT_MAX+INT_MAX会产生数据溢出,结果为负数。举个例子,data[a][b]为1,data[a][c]和data[b][c]都为INT_MAX, 即data[a][b]会被更新为INT_MAX+INT_MAX数据溢出后的负数。针对这个问题可以找一个相对较大的数代替例如1000000,或者进行+等操作前判断是否存在INT_MAX等。

2)不要为了图省事将data[i][i]也置为1000000(自定义的极大值,后面以dmax表达)并且搜索最大数时不排除data[i][i],例如i = j = 2,k = 3,data[i][j] = dmax,data[i][k] = 10,data[j][k] = 10,那么有data[i][j] > data[i][k] + data[k][j],即data[i][j]将被更新为20,而这个20可能会被作为最大数搜索到并输出,佛。。。。。。 

#include <bits/stdc++.h>

using namespace std;

int main()
{
    int num;
    cin >> num;
    vector<vector<int> >data(num + 1, vector<int>(num + 1, 1000000));
    for (int i = 1; i <= num; ++i)
        data[i][i] = 0;
    int a, b, dis;
    while(cin >> a >> b >> dis)
        data[a][b] = data[b][a] = dis;

    //floyd算法
    for (int k = 1; k <= num; ++k)//中转节点
    {
        for (int i = 1; i <= num; ++i)
        {
            for (int j = 1; j <= num; ++j)
            {
                if (data[i][j] > data[i][k] + data[k][j])
                    data[i][j] = data[j][i] = data[i][k] + data[k][j];
                //或写为
                //data[i][j] = data[j][i] = min(data[i][j], data[i][k] + data[k][j]);
            }
        }
    }

    //找出两点之间的最大路径长度
    int mmax = 0;
    for (int i = 1; i <= num; ++i)
    {
        for (int j = 1; j <= num; ++j)
        {
            if (data[i][j] > mmax)
                mmax = data[i][j];
        }
    }

    //输出结果
    int sum = 10 * mmax;
    for (int i = 1;i <= mmax; ++i)
        sum += i;
    cout << sum;
    return 0;
}

DFS算法的解决参照原文章:https://blog.csdn.net/qq_40212930/article/details/88138427?utm_medium=distribute.pc_relevant.none-task-blog-title-2&spm=1001.2101.3001.4242

该方法可以拿到满分。

思路:由题目描述可知,n个城市共n-1条道路,各个城市到首都(1号结点)有且仅有一条道路。所以,可以想到,最长路径path 会包含 从首都结点1到离它最远的结点的一部分。

下面简单证明一下:

假设由1开始dfs的最远结点为i,且path不包含1到i的一部分,那么可能要两种情况:

  情况1:path为1-j且1-k的路径之和。

    但1-i>1-j且1-i>1-k,所以path应该为1-i与1-j或者1-i与1-k之和,故情况1不成立。

  情况2:path不经过1结点。

    1-i>1-k,故m-i>m-k,故情况2也不成立。

所以最长路径path肯定以i为端点,故求出i后再以i为源点DFS出最长的路径path即可。

#include <bits/stdc++.h>

using namespace std;

struct node
{
	int to;
	int cost;
};

//图结点
vector<node> v[1000000];
//标记图结点是否被访问,全局变量默认为false
bool flag[1000000];
//tar保存终点下标, mmax为最长路径距离
int tar, mmax = -1;

void dfs(int s, int sum)
{
	flag[s] = true;
	if(sum > mmax)
		mmax = sum, tar = s;

	for(int i = 0; i < (int)v[s].size(); ++i)
	{
		node t = v[s][i];
		if(flag[t.to] == false)
			dfs(t.to, sum + t.cost);
	}
}

int main()
{
	int num;
	cin >> num;
	int a, b, dis;
	for(int i = 0; i < num - 1; ++i)
	{
	    cin >> a >> b >> dis;
		v[a].push_back(node{b, dis});
		v[b].push_back(node{a, dis});
	}

	dfs(1, 0);
	fill(flag, flag + 1000000, false);
	dfs(tar, 0);

	//输出结果
    cout << mmax * 10 + (1 + mmax) * mmax / 2;

    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MallocLu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值