藍橋杯訓練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;
}

算法分析:

這題是求樹的直徑,那咱們乾脆來說一講這個算法:樹的直徑前端

首先解釋名詞:樹的直徑

由於樹是一種比較特殊的數據結構,樹上的任意兩個點均可以互聯通,而且聯通方式惟一!由於樹是一個最大無環圖,最小聯通圖!那麼既然任意兩個點均可互聯通了,就確定有兩個點之間的距離是全部的兩個點組成的點對的距離中最大的!求這個最大值就是求樹的直徑!
怎樣?樹的直徑是否是就是很符合這個題目的特徵啊?ios

而後說明算法:dfs解樹的直徑

先說明一個道理:

隨便從一個點出發可以到達的最遠的點,必定是樹的直徑的一個端點!
爲何呢?這個能夠反證:假設選取任意點 p
由於樹的直徑的端點到 p 點也是有路徑的,假設端點爲A和B,若是從 p 出發可以到達的最遠的點不是A也不是B,而是C,那麼從A(或者B)出發,可以到達的最遠的點也就不是B(或者A)了!這個你們能夠在紙上畫一下,就可發現!算法

再詳細說算法步驟:

第一次dfs:找到距離出發點p最遠的點A,它是直徑的一個端點
第二次dfs:從直徑的端點出發,找到最遠的點,這個距離就是直徑!markdown

這樣就從一開始的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、天梯賽、西安邀請賽之類的,這些都會在疫情結束以後陸續舉辦,仍是得加緊訓練啊!這幾個題都是不算難的搜索訓練題,那個樹的直徑比較有意思,值得關注!ide

相關文章
相關標籤/搜索