一、P4715 【深基16.例1】淘汰赛
题目大意:能力值高的国家和能力值低的国家踢比赛时高者获胜。1 号国家和 2 号国家踢一场比赛,胜者晋级。3 号国家和 4 号国家也踢一场,胜者晋级……晋级后的国家用相同的方法继续完成赛程,直到决出冠军。给出各个国家的能力值,请问亚军是哪个国家?
思路:如果不用二叉树将非常简单,但还是要用这个知识点。首先能力值可以看做二叉树的最下面的节点,倒数第二层每个节点的值都是子节点值最大的那个。倒数第三层的节点也是两个子节点能力值最大的值,以此类推,倒着用层次建立起二叉树。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int a[10]; //2的几次方
int tree[500]; //数组形式的二叉树
void build(int level) //按照层次建立
{
if (level == 0)
return;
for (int i = a[level - 1]; i < a[level]; i++)
{
tree[i] = max(tree[2 * i], tree[2 * i + 1]);
}
build(level - 1);
}
int main()
{
a[0] = 1;
for (int i = 1; i <= 8; i++)
a[i] = a[i - 1] * 2;
int n;
cin >> n;
for (int i = a[n]; i < a[n + 1]; i++)
cin >> tree[i];
build(n);
int s=min(tree[2], tree[3]); //找到亚军的能力值
for (int i = a[n]; i < a[n + 1]; i++) //扫描得到序号
{
if (tree[i] == s)
{
cout << i - (a[n] - 1) << endl;
return 0;
}
}
}
二、P4913 【深基16.例3】二叉树深度
题目大意:给出每个节点的两个儿子节点,建立一棵二叉树(根节点为 1),如果是叶子节点,则输入0 0。建好树后希望知道这棵二叉树的深度。二叉树的深度是指从根节点到叶子结点时,最多经过了几层。最多有 10^6个结点。
思路:简单的建树,dfs即可
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
struct node {
int l, r;
}a[1000010];
int step_max = 0;
void dfs(int root, int step)//从哪个节点开始深度搜索
{
if (root ==0)
return ;
step_max = max(step_max, step);
dfs(a[root].l, step + 1);
dfs(a[root].r, step + 1);
}
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i].l >> a[i].r;
dfs(1, 1);
cout << step_max << endl;
}
三、P1827 [USACO3.4]美国血统
题目大意:第一行: 树的中序遍历,第二行: 同样的树的前序遍历
输出树的后序遍历
思路 : 递归切割字符串即可
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
//前序遍历:根左右
//中序遍历:左根右
//后序遍历:左右根
string pre, inor;
void work(string pre, string inor)//前序,中序
{
if (pre.empty())return;
char root = pre[0];
int k = inor.find(root);
pre.erase(pre.begin());
string left_pre = pre.substr(0, k);
string right_pre = pre.substr(k);
string left_inor = inor.substr(0, k);
string right_inor = inor.substr(k + 1);
work(left_pre, left_inor);
work(right_pre, right_inor);
cout << root;
}
int main()
{
cin >> inor >> pre;
work(pre, inor);
return 0;
}
四、P5076 【深基16.例7】普通二叉树
题意:一系列的增添改查询操作,个人建议用vector维护一个队列,保证顺序为从大到小。
lower_bound(start,last,n) :返回第一个大于等于n的地址
upper_bound(start, last, n) :返回第一个大于n的地址
#include<iostream>
#include<cstdio>
#include<algorithm>
#include <vector>
using namespace std;
int main()
{
int n, option, operand;
cin >> n;
vector< int> v;
for (int i = 1; i <= n; i++)
{
cin >> option >> operand;//选项,操作数
if (option == 1)
{
auto it= lower_bound(v.begin(), v.end(), operand);
cout << it - v.begin() + 1 << endl;
}
if (option == 2)
{
cout << v[operand - 1] << endl;
}
if (option == 3)
{
auto it = lower_bound(v.begin(), v.end(), operand);
cout << *(it - 1) << endl;
}
if (option == 4)
{
auto it = upper_bound(v.begin(), v.end(), operand);
if (it != v.end()) cout << *it << endl;
else cout << "2147483647" << endl;
}
if (option == 5)
{
if (v.empty())v.push_back(operand);
else
{
auto it = upper_bound(v.begin(), v.end(), operand);
v.insert(it, operand);
}
}
}
}
五、P1364 医院设置
题目大意,第一行一个整数 n,表示树的结点数。接下来的 n行每行描述了一个结点的状况,包含三个整数 w,u,v,其中 w为居民人口数,u为左链接(为 0表示无链接),v为右链接(为 0 表示无链接)。现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,输出最小距离和。
思路:用Floyed算法跑一遍两个节点的最小距离然后暴力就可以了。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define MAX 100000007
using namespace std;
int people[101],dis[101][101];
int main()
{
int n, l, r;
cin >> n;
memset(dis, MAX, sizeof(dis));
for (int i = 1; i <= n; i++)
{
dis[i][i] = 0;
cin >> people[i] >> l >> r;
if (l)dis[i][l] = dis[l][i] = 1;
if (r)dis[i][r] = dis[r][i] = 1;
}
//Floyed算法
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for (int k = 1; k <= n; k++)
{
if (j != k && dis[j][k] > dis[j][i] + dis[i][k])
dis[j][k] = dis[j][i] + dis[i][k];
}
int end_min = MAX;
for (int i = 1; i <= n; i++)
{
int sum = 0;
for (int j = 1; j <= n; j++)
{
if (i != j && dis[i][j] != -1) sum += people[j] * dis[i][j];
}
end_min = min(end_min, sum);
}
cout << end_min << endl;
}
六、P1229 遍历问题
题目大意:第一行表示该二叉树的前序遍历结果s1,第二行表示该二叉树的后序遍历结果s2。求可能的中序遍历序列的总数
思路:只有一个儿子的节点 ,才会在知道前序后序的情况下有不同的中序遍历,所以将题目转化成找只有一个儿子的节点个数。(前序中出现AB,后序中出现BA,则这个节点只有一个儿子)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
using namespace std;
int main()
{
char str1[1000], str2[1000];
//先序,后序
cin >> str1 >> str2;
int ans = 1;
for (int i = 0; i < strlen(str1)-1; i++)
{
for (int j = 0; j < strlen(str2); j++)
{
if (str1[i] == str2[j] && str1[i + 1] == str2[j - 1])
ans*=2;
}
}
cout << ans << endl;
}
七、P1305 新二叉树
题目大意:后面 n 行,每一个字母为节点,后两个字母分别为其左右儿子。输出其前序序列。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
struct node {
char lc, lr;//左右儿子
}l[200];
void bfs(char x)
{
if (x == '*')
return;
cout << x;
bfs(l[x].lc);
bfs(l[x].lr);
}
int main()
{
int n;
cin >> n;
char root,h;
cin >> root;
cin >> l[root].lc;
cin >> l[root].lr;
for (int i = 2; i <= n; i++)
{
cin >> h;
cin>>l[h].lc >> l[h].lr;
}
bfs(root);
}
八、P1030 求先序排列
给出一棵二叉树的中序与后序排列。求出它的先序排列。(约定树结点用不同的大写字母表示,长度≤8)。
此题和第三题一样
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
void work(string inor,string after)
{
if (inor.empty())return;
char root= after[after.size() - 1];
cout << root;
int k = inor.find(root);
after.erase(after.end()-1);
string left_after = after.substr(0, k);
string right_after = after.substr(k);
string left_inor = inor.substr(0, k);
string right_inor = inor.substr(k + 1);
work( left_inor, left_after);
work(right_inor, right_after);
}
int main()
{
string inor, after;
cin >> inor >> after;
work(inor, after);
}
九、P3884 [JLOI2009]二叉树问题
题目大意:给出一堆节点,求出树的深度,宽度,以及u,v两个节点之间的距离。
思路:第一个求深度可以在输入是自行更新值,而求宽度也简单,扫描一面求除最大值,求u,v两节点的距离重点在于求出他们最近的公共祖父,那就让两个节点一直向上,遍历即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
struct node
{
int f;
int l, r;
int deep,flag;//深度,记录走过没
}a[10001];
int sum[101];
int found(int x, int y)
{
a[x].flag = 1;
while (a[x].f != 0)
{
x = a[x].f;
a[x].flag = 1;
}
while (a[y].flag !=1 )
{ //遍历至x节点已走过的节点,找到最近公共祖先
y = a[y].f;
}
return y;
}
int main()
{
int n,x,y,deep_max=0;
cin >> n;
a[1].f =a[1].deep= 0;
for (int i = 1; i < n; i++)
{
cin >> x >> y;
if (a[x].l == 0)
{
a[x].l = y;
}
else
a[x].r = y;
a[y].deep = a[x].deep+1;
a[y].f = x;
deep_max = max(deep_max, a[y].deep);
}
cin >> x >> y;
int ancestor = found(x, y),num=0;
while (x != ancestor)
{
x = a[x].f;
num++;
}
num *= 2;
while (y != ancestor)
{
y= a[y].f;
num++;
}
for (int i = 1; i <= n; i++) //把每一个深度有多少个节点记录
sum[a[i].deep]++;
sort(sum + 1, sum + 1 + 100);
cout << ++deep_max << endl << sum[100] << endl << num; //sum[100]是最大的宽度节点个数
}