复旦机考
题目类型:图论
#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;
}