蓝桥杯训练1:搜索专题真题(一道树的直径比较有趣)

第一题:带分数

训练题1:带分数

我的AC代码:

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

int GetNum(int a[], int begin, int end) {
    
    
	int num = 0;
	for (int i = begin; i < end; ++i)
		num = num * 10 + a[i];
	return num;
}

int main() {
    
    
	int n, len = 0, a[9] = {
    
     1, 2, 3, 4, 5, 6, 7, 8, 9 };
	cin >> n;
	int Temp = n, ans = 0;
	while (Temp) {
    
    
		Temp /= 10;
		len++;
	}
	do {
    
    
		int num = 0;
		for (int i = 0; i < len; ++i) {
    
    
			num = num * 10 + a[i];
			if(num >= n)
				break;
			int k = n, j = 9 - (i + 1);
			k -= num;
			int mid = (j >> 1) + 1;
			while (mid < 9) {
    
    
				int num1 = GetNum(a, i + 1, mid);
				int num2 = GetNum(a, mid, 9);
				if (0 == num1 % num2 && k == num1 / num2)
					ans++;
				mid++;
			}
		}
	} while (next_permutation(a, a + 9));
	cout << ans;
	return 0;
}

第二题:剪格子

训练题2:剪格子

我的AC代码:

#include <iostream>
using namespace std;

const int maxn = 12;
int n, m, a[maxn][maxn], vis[maxn][maxn], sum, ans;
int dir[4][2] = {
    
     {
    
    0, 1},{
    
    0,-1},{
    
    1,0},{
    
    -1,0} };
void dfs(int x, int y, int cnt, int blank) {
    
    
	if (cnt * 2 >= sum) {
    
    
		if (blank > ans && cnt * 2 == sum)
			ans = blank;
		return;
	}
	for (int i = 0; i < 4; ++i) {
    
    
		int dx = x + dir[i][0], dy = y + dir[i][1];
		if (dx >= 0 && dx < n && dy >= 0 && dy < m) {
    
    
			if (!vis[dx][dy]) {
    
    
				vis[dx][dy] = 1;
				dfs(dx, dy, cnt + a[dx][dy], blank + 1);
				vis[dx][dy] = 0;
			}
		}
	}
}
int main() {
    
    
	cin >> m >> n;
	for(int i = 0;i < n;++i)
		for (int j = 0; j < m; ++j)
		{
    
    
			cin >> a[i][j];
			sum += a[i][j];
		}
	if (sum & 1)
		cout << 0;
	else {
    
    
		dfs(0, 0, a[0][0], 1);
		cout << ans;
	}
	return 0;
}

第三题:大臣的旅费

大臣的旅费

我的75分纯暴力做法(超时):

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;

const int maxn = 10005;
struct Edge {
    
    
	int dest, cost;
	Edge(int d, int c) :dest(d), cost(c) {
    
      }
};
vector<Edge> G[maxn];
int n, ans, cnt, vis[maxn];
int dfs(int p, int Len) {
    
    
	if (vis[p]) 
		return cnt;
	if (cnt < Len)
		cnt = Len;
	vis[p] = 1;
	for (int i = 0; i < (int)G[p].size(); ++i) 
		if (!vis[G[p][i].dest])
			dfs(G[p][i].dest, Len + G[p][i].cost);
	return cnt;
}
int main() {
    
    
	scanf("%d", &n);
	for (int i = 1; i < n; ++i) {
    
    
		int a, b, c;
		scanf("%d %d %d", &a, &b, &c);
		G[a].push_back(Edge(b, c));
		G[b].push_back(Edge(a, c));
	}
	for (int i = 1; i <= n; ++i) {
    
    
		int Temp = dfs(i, 0);
		if (ans < Temp)
			ans = Temp;
		cnt = 0;
		memset(vis, 0, sizeof(vis));
	}
	printf("%d", ans * 10 + (ans + 1) * ans / 2);
	return 0;
}

算法分析:

这题是求树的直径,那我们干脆来讲一讲这个算法:树的直径

首先解释名词:树的直径

因为树是一种比较特殊的数据结构,树上的任意两个点都可以互联通,并且联通方式唯一!因为树是一个最大无环图,最小联通图!那么既然任意两个点都可互联通了,就肯定有两个点之间的距离是所有的两个点组成的点对的距离中最大的!求这个最大值就是求树的直径!
怎样?树的直径是不是就是很符合这个题目的特征啊?

然后说明算法:dfs解树的直径

先说明一个道理:

随便从一个点出发能够到达的最远的点,一定是树的直径的一个端点!
为什么呢?这个可以反证:假设选取任意点 p
因为树的直径的端点到 p 点也是有路径的,假设端点为A和B,如果从 p 出发能够到达的最远的点不是A也不是B,而是C,那么从A(或者B)出发,能够到达的最远的点也就不是B(或者A)了!这个大家可以在纸上画一下,就可发现!

再详细说算法步骤:

第一次dfs:找到距离出发点p最远的点A,它是直径的一个端点
第二次dfs:从直径的端点出发,找到最远的点,这个距离就是直径!

这样就从一开始的n次dfs到2次dfs,就能通过了!

我的AC代码:

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;

const int maxn = 10005;
struct Edge {
    
    
	int dest, cost;
	Edge(int d, int c) :dest(d), cost(c) {
    
      }
};
vector<Edge> G[maxn];
int n, ans = -1, MaxPoint = -1, vis[maxn];
void dfs(int p, int Len) {
    
    
	if (vis[p]) 
		return ;
	if (ans < Len) {
    
    
		ans = Len;
		MaxPoint = p;
	}
	vis[p] = 1;
	for (int i = 0; i < (int)G[p].size(); ++i) 
		if (!vis[G[p][i].dest])
			dfs(G[p][i].dest, Len + G[p][i].cost);
	return ;
}
int main() {
    
    
	scanf("%d", &n);
	for (int i = 1; i < n; ++i) {
    
    
		int a, b, c;
		scanf("%d %d %d", &a, &b, &c);
		G[a].push_back(Edge(b, c));
		G[b].push_back(Edge(a, c));
	}
	dfs(1, 0);
	ans = -1;
	memset(vis, 0, sizeof(vis));
	dfs(MaxPoint, 0);
	printf("%d", ans * 10 + (ans + 1) * ans / 2);
	return 0;
}

练习总结:

说实话,前端时间大部分精力都放在了密码学的研究和编码方面去了,其次就是搞面向对象的程序设计练习去了,已经有好一阵子没做算法训练了(惭愧!)由于即将到来蓝桥杯、还有csp、天梯赛、西安邀请赛之类的,这些都会在疫情结束之后陆续举办,还是得加紧训练啊!这几个题都是不算难的搜索训练题,那个树的直径比较有意思,值得关注!

猜你喜欢

转载自blog.csdn.net/qq_44274276/article/details/104844474
今日推荐