7-1 二叉树最长路径 (100 分)
给定一棵二叉树T,求T中的最长路径的长度,并输出此路径上各结点的值。若有多条最长路径,输出最右侧的那条。
输入格式:
第1行,1个整数n,表示二叉树有n个结点, 1≤n≤100000.
第2行,2n+1个整数,用空格分隔,表示T的扩展先根序列, -1表示空指针,结点用编号1到n表示。
输出格式:
第1行,1个整数length,length表示T中的最长路径的长度。
第2行,length+1个整数,用空格分隔,表示最右侧的最长路径。
输入样例:
在这里给出一组输入。例如:
5
1 2 -1 -1 3 4 -1 -1 5 -1 -1
输出样例:
在这里给出相应的输出。例如:
2
1 3 5
思路:
这道题是要先用BST建一棵树,然后写一个判断最长路径的函数,运用递归求出长度,有一个记录路径的对列q,从树根开始,先让根入队,再判断高度,指针指向最高的结点,递归,到最后队列的内容就是最长路径
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<stdlib.h>
#include<queue>
using namespace std;
queue < int>q;
struct tree {
int data;
struct tree* left;
struct tree* right;
};
int length;
struct tree* s;
struct tree* CBT(int tostop) {
int x;
cin >> x;
if (x == tostop) {
return NULL;
}
else {
struct tree* t;
t = (struct tree*)malloc(sizeof(struct tree));
t->data = x;
t->left = CBT(tostop);
t->right = CBT(tostop);
return t;
}
}
int longest(struct tree* t) {
if (t == NULL) {
return 0;
}
else {
int left, right;
left = longest(t->left);
right = longest(t->right);
return (left > right ? left : right) + 1;
}
}
void road(struct tree* t) {
if (t == NULL) {
return;
}
s = t;
while (s != NULL) {
q.push(s->data);
if (s->left == NULL || (s->left != NULL && s->right != NULL && longest(s->right) >= longest(s->left))) {
s = s->right;
}
else {
s = s->left;
}
}
}
struct tree* root = NULL;
int n;
int main() {
cin >> n;
root = CBT(-1);
length = longest(root);
cout << length - 1 << endl;
road(root);
while (!q.empty()) {
cout << q.front();
q.pop();
if (!q.empty()) {
cout << " ";
}
}
return 0;
}
7-2 森林的层次遍历 (100 分)
给定一个森林F,求F的层次遍历序列。森林由其先根序列及序列中每个结点的度给出。
输入格式:
第1行,1个整数n,表示森林的结点个数, 1≤n≤100000.
第2行,n个字符,用空格分隔,表示森林F的先根序列。字符为大小写字母及数字。
第3行,n个整数,用空格分隔,表示森林F的先根序列中每个结点对应的度。
输出格式:
1行,n个字符,用空格分隔,表示森林F的层次遍历序列。
输入样例:
在这里给出一组输入。例如:
14
A B C D E F G H I J K L M N
4 0 3 0 0 0 0 2 2 0 0 0 1 0
输出样例:
在这里给出相应的输出。例如:
A M B C G H N D E F I L J K
思路:
这道题涉及到的是森林,而不是独立的一棵树,所以需要多次构建树,用一个x来记录已经用过的结点的个数,当它等于n时,森林就建完了,让每棵树的根入队列。在建树的时候是用vector,根据结点所对应的度,依次读后面的数字为它的子结点(这里需要用到递归,因为它的子结点也可能有子结点)
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
int n, x = 0;
char letter[100000];
int du[100000];
vector<int>son[100000];
int buildtree() {
int num, i;
num = x++;
for (i = 0; i < du[num]; i++) {
son[num].push_back(buildtree());
}
return num;
}
int main() {
int i;
cin >> n;
for (i = 0; i < n; i++) {
cin >> letter[i];
}
for (i = 0; i < n; i++) {
cin >> du[i];
}
queue<int>q;
while (x != n) {
int k;
k = buildtree();
q.push(k);//每棵树的根节点
}
while (!q.empty()) {
int y;
y = q.front();
q.pop();
cout << letter[y];
for (i = 0; i < du[y]; i++) {
q.push(son[y][i]);
}
cout <<( !q.empty() ? ' ' : '\n');
}
}
7-3 纸带切割 (100 分)
有一条细长的纸带,长度为 L 个单位,宽度为一个单位。现在要将纸带切割成 n 段。每次切割把当前纸带分成两段,切割位置都在整数单位上,切割代价是当前切割纸带的总长度。每次切割都选择未达最终要求的最长纸带切割,若这样的纸带有多条,则任选一条切割。如何切割,才能完成任务,并且总代价最小。
输入格式:
第1行,1个整数n,表示切割成的段数, 1≤n≤100000.
第2行,n个整数Li,用空格分隔,表示要切割成的各段的长度,1≤Li≤200000000,1≤i≤n.
输出格式:
第1行,1个整数,表示最小的总代价。
第2行,若干个整数,用空格分隔,表示总代价最小时每次切割的代价。
输入样例:
在这里给出一组输入。例如:
5
5 6 7 2 4
输出样例:
在这里给出相应的输出。例如:
54
24 13 11 6
思路:
这道题可以根据给出的样例看出这是一道关于哈弗曼树的题,就是构建给定序列的哈夫曼树,接着按照合并顺序输出内结点的值。
#include<bits/stdc++.h>
using namespace std;
int n;
vector<int>a;
int b[100001];
stack<int>s;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
long long sum = 0;
int k, x, y;
cin >> n;
if (n == 1) {
cout << "0" << endl;
exit(0);
}
for (k = 0; k < n; k++) {
cin >> b[k];
a.push_back(b[k]);
}
priority_queue<int, vector<int>, greater<int> >q(a.begin(), a.end());
while (q.size() != 1) {
x = q.top();
q.pop();
y = q.top();
q.pop();
sum = sum + x + y;
s.push(x + y);
q.push(x + y);
}
cout << sum << endl;
while (s.size() != 1) {
cout << s.top() << " ";
s.pop();
}
cout << s.top() << endl;
}
7-4 序列乘积 (100 分)
两个递增序列A和B,长度都是n。令 Ai 和 Bj 做乘积,1≤i,j≤n.请输出n*n个乘积中从小到大的前n个。
输入格式:
第1行,1个整数n,表示序列的长度, 1≤n≤100000.
第2行,n个整数Ai,用空格分隔,表示序列A,1≤Ai≤40000,1≤i≤n.
第3行,n个整数Bi,用空格分隔,表示序列B,1≤Bi≤40000,1≤i≤n.
输出格式:
1行,n个整数,用空格分隔,表示序列乘积中的从小到大前n个。
输入样例:
在这里给出一组输入。例如:
5
1 3 5 7 9
2 4 6 8 10
输出样例:
在这里给出相应的输出。例如:
2 4 6 6 8
思路:
这道题的其实并不难,但是很容易被卡时间,我之前用的是暴力求解,就是把n²个全都求出来,这就很费时间。但是这道题的数据本就是按递增序列排的,所以可以先求出第一行第一个数与第二行所有数的乘积并全部压入递减的优先队列中,接下来就是用对列头(最大的数)跟第一行下一个数与第二行第一个数的乘积相比较,若队头更大,则队头出队,乘积入队,保证了队列中一直都只有n个数,若队头较小,则直接退出循环,因为接下来的乘积只会更大,没必要进行下去。最后要记得把队列的顺序颠倒一下,因为要求的输出是递增的,而队列是递减的
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<queue>
#include<iostream>
#include<stdlib.h>
using namespace std;
priority_queue < int, vector<int>, less<int>>q;
int a[100001], b[100001];
int n;
int* x;
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
int i, j;
for (i = 0; i < n; i++) {
cin >> a[i];
}
for (j = 0; j < n; j++) {
cin >> b[j];
}
for (j = 0; j < n; j++) {
q.push(a[0] * b[j]);
}
for (i = 1; i < n; i++) {
for (j = 0; j < n; j++) {
if (a[i] * b[j] < q.top()) {
q.pop();
q.push(a[i] * b[j]);//放入队列后又会重新排序
}
else {//b[j]越来越大,没必要进行j++
break;
}
}
}
x = (int*)malloc(sizeof(int) * n);
for (i = 0; i < n; i++) {
x[n - i - 1] = q.top();
q.pop();
}
for (i = 0; i < n; i++) {
cout << x[i];
if (i < n - 1) {
cout << " ";
}
}
return 0;
}