算法刷题记录(Day 78)

本文解析了复旦机考中的一道图论题目,介绍了如何利用并查集和贪心算法解决该问题,并提供了完整的代码实现。此外,还讨论了南大2019年的三道算法题,包括数字移除后的最小值问题、男孩女孩排队问题及二叉树构造数目问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

复旦机考

题目类型:图论
在这里插入图片描述

#include<iostream>
#include<vector>
#include<set>
#include<algorithm>
using namespace std;
#define NMAX 110
int N, M, K;
set<int > B;
vector<pair<int, pair<int, int>> >E;
int fa[NMAX];
int find(int x) {
	if (x != fa[x]) fa[x] = find(fa[x]);
	return fa[x];
}
int main() {
	cin >> N >> M >> K;
	int num = N;//初始有N个连通分量
	for (int i = 1; i <= N; i++) fa[i] = i;
	for (int i = 0; i < K; i++) {
		int cur;
		cin >> cur;
		B.insert(cur);
	}
	for (int i = 1; i <= M; i++) {
		int u, v, d;
		cin >> u >> v >> d;
		if (B.find(i) != B.end()) {
			int fa1 = find(u), fa2 = find(v);
			if (fa1 != fa2) fa[fa1] = fa2;
			num--;
		}
		else {
			E.push_back(make_pair(d, make_pair(u, v)));
		}
	}
	sort(E.begin(), E.end());
	
	int res = 0;
	for (int i = 0; i < E.size(); i++) {
		int u, v, d;
		u = E[i].second.first, v = E[i].second.second, d = E[i].first;
		int fa1 = find(u), fa2 = find(v);
		if (fa1 != fa2) {
			fa[fa1] = fa2;
			res += d;
			num--;
		}
		if (!num) break;
	}
	if (num) cout << -1 << endl;
	else cout << res << endl;
}

南大2019题1

给你一个不超过100位的数n,和一个不超过100的数字k,要求从数n中去掉k个数字,然后使得去掉k个数之后,n最小。

解题思路:去掉数字k后得到的位数都是一样的,目标是使得最高位的数字最小
不能说是将所有的最大的数字都去掉
使用贪心的思想,每次都去找找在当前可以去掉的数字的情况下能够得到的最小的数字。

//89819279137121221340121  10 1111221340121
//98900028919018928912398716352678901320713628301283041 22 111267801320713628301283041
//278000120000 12 0
#include<iostream>
#include<string>
using namespace std;
#define NMAX 110
int num[NMAX];
int length = 1, k, pointer=0;
int d[NMAX][10];
string res;
void init() {
	memset(d, 0xff, sizeof(d));
	for (int i = 0; i <= length; i++) {
		for (int j = i + 1; j <= length; j++) {
			if (d[i][num[j]] < 0) d[i][num[j]] = j - i - 1;
		}
	}

}
int main() {
	char c;
	while ((c = getchar()) != ' ') {
		num[length++] = c - '0';
	}
	cin >> k;
	init();
	//使用贪心的思路
	int flag = 0;
	while (k && pointer!=length-1) {
		for (int i = 0; i <= 9; i++) {
			if (d[pointer][i] <= k && d[pointer][i] >=0) {
				if (i != 0) flag = 1;
				if (flag) {
					char cur = i + '0';
					res += cur;
				}
				k -= d[pointer][i];
				pointer += (d[pointer][i] + 1);
				break;
			}
		}
		//cout << res << endl;
	}
	for (int i = pointer + 1; i < length; i++) {
		char cur = num[i] + '0';
		res += cur;
	}
	if (!res.size()) cout << 0 << endl;
	else cout << res << endl;
}

南大2019题2

有B个男孩,G个女孩,要求所有男孩女孩排成一队,连续的男孩个数不可以超过K个,问一共有多少种排法。(结果需要mod 10007)

使用dp的思路来做。
dp[i][j]代表有i个男孩j个女孩的排法个数,存在如下的递推公式
首先,拿出一个女孩,那么还剩下i个男孩和j-1个女孩,最后一个女孩不会影响前面的排法,由此第一部分包含dp[i][j-1]
然后,再拿出一个男孩,那么还剩下i-1个男孩和j个女孩,在这个划分下,需要去除掉最后为一个女孩加上k-1个男孩的情况,即第二部分为dp[i-1][j]-dp[i-k][j-1]

//dp求法
#include<iostream>
using namespace std;
#define NMAX 1000
#define MOD 10007
int B, G, K;
int dp[NMAX][NMAX];
int main() {
	cin >> B >> G >> K;
	memset(dp, 0, sizeof(dp));
	dp[0][0] = 1;
	for (int i = 1; i <= B; i++) {
		if (i <= K) dp[i][0] = 1;
		else dp[i][0] = 0;
	}
	for (int i = 1; i <= G; i++) dp[0][i] = 1;
	for (int i = 1; i <= B; i++) {
		for (int j = 1; j <= G; j++) {
			dp[i][j] = dp[i][j]+dp[i][j - 1]+dp[i-1][j];
			 if (i > K) dp[i][j] -= dp[i - K -1][j - 1];
			 if (dp[i][j] < 0) {
				 cout << "hi";
			}
		}
	}
	cout << dp[B][G] % MOD<< endl;
}
//爆搜的做法
#include<iostream>
using namespace std;
#define MOD 10007
int B, G, K;
int res = 0;
void DFS(int b, int g, int k) {
	if (b + g == B + G) {
		res = (res + 1) % MOD;
	}
	if (b < B && k!=K) DFS(b + 1, g, k + 1);
	if (g < G) DFS(b, g + 1, 0);
}
int main() {
	cin >> B >> G >> K;
	DFS(0, 0, 0);
	cout << res << endl;
}

南大2019题3

题目:
给出一个二叉树的前序遍历序列和后序遍历序列,序列是没有空节点#号的,只有字母,问通过这两个序列可以构造多少中不同的二叉树,因为树的样子不一样,遍历的序列是可能一样的。比如前序序列:ABC,后序序列CBA,就有4种不同的树

解题思路:参考思路
对于前序和后序遍历得到的中序序列的不确定性来源于当一个节点只有一个孩子节点时该孩子节点应该是左节点还是右孩子节点。(当一个节点有左右两个孩子的时候就不存在这样的问题,对于以A为根,以B为左孩子节点,以C为右孩子节点的树来说,其前序遍历的序列为ABC,后序遍历的序列为BCA)
因此,需要确定该种节点的个数。假如a[i]=b[j],a[i+1]=b[j-1],那么就有节点a[i]仅有一个孩子节点a[i+1]或者b[j-1]。

#include<iostream>
#include<algorithm>
using namespace std;
string A, B;
int res = 0;
int main() {
	cin >> A >> B;
	for (int i = 0; i < A.size(); i++) {
		for (int j = 0; j < B.size(); j++) {
			if (A[i] == B[j] && i + 1 < A.size() && j - 1 >= 0) {
				if (A[i + 1] == B[j - 1]) res++;
			}
		}
	}
	cout << pow(2, res) << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值