参考资料:《算法笔记》,柳神csdn博客
PAT-A1020
问题:给定后序和中序序列,要求给出层序序列。
思路:根据后序和中序序列建树然后再层序遍历输出即可。
关键:在建树过程中要考虑边界条件,即后序序列为空时if(preL>preR) return NULL 退出。
PAT-A1021
问题:给定一张图的边信息,如果这是一个树,给出deepest root(使树高最高的那个根),否则求出这图有几个连通分量。
思路:DFS
关键:用vector代替二维数组以避免TLE
#include<cstdio>
#include<vector>
using namespace std;
const int maxn = 10010;
vector<int> a[maxn];
int N,book[maxn],depth[maxn];
void dfs(int root){
book[root] = 1;
for(auto i:a[root]){
if(book[i]==0){
dfs(i);
}
}
}
vector<int> path;
void dfs2(int root){
book[root] = 1;
path.push_back(root);
if(path.size()>depth[root])
depth[root] = path.size();
for(auto i:a[root]){
if(book[i]==0)
dfs2(i);
}
path.pop_back();
}
int main(){
scanf("%d",&N);
int p,q;
for(int i=0;i<N;i++){
scanf("%d%d",&p,&q);
a[p].push_back(q);
a[q].push_back(p);
}
int comp = 0;
for(int i=1;i<=N;i++){
if(book[i]==0){
comp++;
dfs(i);
}
}
if(comp!=1){
printf("Error: %d components",comp);
return 0;
}
for(int i=1;i<=N;i++){
for(int j=1;j<=N;j++)
book[j]=0;
if(book[i]==0){
dfs2(i);
}
}
vector<int> v;
int max=0;
for(int i=1;i<=N;i++){
if(depth[i]>max){
max = depth[i];
v.clear();
v.push_back(i);
}else if(depth[i]==max){
v.push_back(i);
}
}
for(int i=0;i<v.size();i++)
printf("%d\n",v[i]);
return 0;
}
PAT-A1053【hard】
问题:给定一个每个结点都有权值的树并给定一个整数S,要求求出从根到叶子的路径上所有结点权值之和为这个整数S的所有这样的路径,并按照权值降序排列输出。
思路:根据给定权值和信息建树,通过静态方式建树最方便,然后进行DFS来搜索。
关键:创建一个path变量用于记录当前搜索路径上的各个结点在书中的下标;创建一个与path变量对应的sum变量用于记录path路径上的权值之和,与给定的整数S进行比较以进行剪枝。DFS部分代码如下:
vector<int> path;//这里path用vector来实现
void DFS(int index,int sum){//index表示path中的最后一个结点的下标
if(sum > S)
return ;
if(sum==S){
if(Node[index].children.size()!=0)
return;//不是叶子结点
int numNode = path.size();
for(int i=0;i<numNode;i++){
printf("%d", Node[path[i]].weight);
if(i<numNode-1)
printf(" ");
else
printf("\n");
}
return;
}
//sum < S的情况
for(int i=0;i<Node[index].children.size();i++){
int child = Node[index].children[i];
path.push_back(child);
DFS(child,sum+Node[child].weight);
path.pop_back();
}
}
PAT-A1086
问题:给定先序序列以及通过栈来获得的中序序列,要求给出后序序列
思路:先通过栈模拟获得中序序列,然后根据先序和中序求后序。
关键:
1.cstring中的strcmp可以用来判断字符串。字符串用char a[12]这样来存储。
2.这里可以像A1020那样先建树,也可以直接求出。后者的关键部分代码如下:
void DFS(int root,int inL,int inR){
/*
在每次的DFS中:
这里的root表示先序序列pre的根结点的下标
不需要建树,只需将每次得到的根结点记录即可。
*/
if(inL > inR) return;
int k=inL;
for(;k<=inR&&pre[root]!=in[k];k++);
int numL = k - inL;
DFS(root+1,inL,k-1);
DFS(root+1+numL,k+1,inR);
post.push_back(pre[root]);
}
//类似的,修改此处代码即可根据后序和中序得到先序,当前root也要从后往前。
DFS(root+1,inL,k-1);
DFS(root+1+numL,k+1,inR);
post.push_back(pre[root]);
疑惑:如何根据层序和中序获得后序?
PAT-A1102
问题:根据信息建树并给出inverted(按垂直轴旋转180度)后的树的层序和中序遍历
思路:建树然后lchild和rchild先后调换顺序即可
关键:对“-”的处理,对内存使用的限制
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
const int maxn = 15;
struct node{
int lchild;
int rchild;
}Node[maxn];
queue<int> q;
int num1=0,num2=0,n;
void Level(int root){
q.push(root);
while(!q.empty()){
int now = q.front();
q.pop();
printf("%d",now);
if(++num1<n)
printf(" ");
if(Node[now].rchild!=-1)
q.push(Node[now].rchild);
if(Node[now].lchild!=-1)
q.push(Node[now].lchild);
}
}
void In(int root){
if(root==-1)
return;
In(Node[root].rchild);
printf("%d",root);
if(++num2<n)
printf(" ");
In(Node[root].lchild);
}
int main(){
scanf("%d",&n);
string l,r;
bool NotRoot[n]={false};
for(int i=0;i<n;i++){
cin >> l >> r;
if(l!="-"){
Node[i].lchild = stoi(l);
NotRoot[Node[i].lchild] = true;
}else{
Node[i].lchild = -1;
}
if(r!="-"){
Node[i].rchild = stoi(r);
NotRoot[Node[i].rchild] = true;
}else{
Node[i].rchild = -1;
}
}
int k;
for(int i=0;i<n;i++){
if(!NotRoot[i]){
k = i;
break;
}
}
Level(k);
printf("\n");
In(k);
return 0;
}
PAT-A1079
问题:在供应链的背景下,给出供应链上的树型关系以及货量,要求求出最终经销商的销售额。
思路:在用静态建树后遍历的同时记录下当前路径的depth即可。
关键:难度适中
一发AC代码:
#include<iostream>
#include<vector>
#include<cmath>
using namespace std;
const int maxn = 100000;
int N;
double P,r;
struct node{
int w;
vector<int> child;
}a[maxn];
double sum = .0;
void DFS(int root,int depth){
if(a[root].child.empty()){
sum += pow((1+r/100),depth-1)*P*a[root].w;
return;
}
for(auto i:a[root].child){
DFS(i,depth+1);
}
}
int main(){
scanf("%d%lf%lf",&N,&P,&r);
int n,m;
for(int i=0;i<N;i++){
scanf("%d",&n);
if(n==0){
scanf("%d",&m);
a[i].w = m;
continue;
}
for(int j=0;j<n;j++){
scanf("%d",&m);
a[i].child.push_back(m);
}
}
DFS(0,1);
printf("%.1f",sum);
}
PAT-A1090
问题:与A1079类似
思路:反向思维,a的供应商是b,即b供应给a,用这样的思路来建树即可。
关键:难度适中
一发AC代码如下:
#include<iostream>
#include<vector>
#include<cmath>
using namespace std;
const int maxn = 100000;
int N,n,root,maxDepth=-1,maxNum=0;
double P,r;
vector<int> a[maxn];
void DFS(int root,int depth){
if(a[root].empty()){
if(depth>maxDepth){
maxDepth = depth;
maxNum = 1;
}else if(depth==maxDepth){
maxNum++;
}
return;
}
for(auto i:a[root]){
DFS(i,depth+1);
}
}
int main(){
scanf("%d%lf%lf",&N,&P,&r);
r/=100;
for(int i=0;i<N;i++){
scanf("%d",&n);
if(n!=-1)
a[n].push_back(i);
else{
root = i;
}
}
DFS(root,1);
double sum = pow(1+r,maxDepth-1)*P;
printf("%.2f %d",sum,maxNum);
}
PAT-A1094
问题:给定一个族谱关系,要求求出一个每一个level的人数和中最大的那一个level以及这个数量。
思路:静态建树,一个DFS进行遍历的同时,加入level并统计。
关键:DFS时加入level参数。同时要注意在读取数据时,变量要一个个仔细对应上,不然浪费太多时间debug。
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
const int maxn = 110;
vector<int> a[maxn];
int p[maxn]={0},N,M,maxP=0,maxL;
void DFS(int root,int level){
p[level]++;
for(int i=0;i<a[root].size();i++){
DFS(a[root][i],level+1);
}
}
int main(){
scanf("%d%d",&N,&M);
int n,m,k;
for(int i=0;i<M;i++){
scanf("%d%d",&n,&m);
for(int j=0;j<m;j++){
scanf("%d",&k);
a[n].push_back(k);
}
}
DFS(1,1);
for(int i=1;i<maxn;i++){
if(p[i]>maxP){
maxP = p[i];
maxL = i;
}
}
printf("%d %d",maxP,maxL);
return 0;
}
PAT-A1130
问题:给一个二叉语法树要求给出其中缀表达式。
思路:建树,中序遍历求出中缀表达式即可。
关键:
1.用string来存储结点信息data
2.不要混用printf与cout 以及scanf与cin 不然会出错。
3.处理括号时可以举一些例子来帮助判断。
#include<iostream>
using namespace std;
const int maxn = 30;
struct node{
string data;
int lchild;
int rchild;
}a[maxn];
int N,book[maxn];
void dfs(int root){
if(root==-1)return;
if(a[root].lchild==-1&&a[root].rchild==-1) {
cout << a[root].data;
return;
}
cout << "(";
dfs(a[root].lchild);
cout << a[root].data;
dfs(a[root].rchild);
cout << ")";
}
int main(){
cin >> N;
string str;
int p,q,book[N+1]={0};
for(int i=1;i<=N;i++){
cin >> str >> p >> q;
a[i].data = str;
a[i].lchild = p;
a[i].rchild = q;
if(p!=-1)book[p]=1;
if(q!=-1)book[q]=1;
}
int root=1;
while(book[root]!=0)
root++;
dfs(a[root].lchild);
cout << a[root].data;
dfs(a[root].rchild);
return 0;
}
PAT-A1106
与A1090是镜像问题、输入数据格式与A1079类似,稍加修改即可。
#include<iostream>
#include<vector>
#include<cmath>
using namespace std;
const int maxn = 100010;
int N,minDepth=maxn,minNum;
double P,r,sum=0;
vector<int> a[maxn];
void DFS(int root,int depth){
if(a[root].empty()){
if(depth<minDepth){
minDepth = depth;
minNum = 1;
}else if(depth == minDepth){
minNum++;
}
return;
}
for(int i=0;i<a[root].size();i++){
DFS(a[root][i],depth+1);
}
}
int main(){
scanf("%d%lf%lf",&N,&P,&r);
r/=100;
int n,m;
for(int i=0;i<N;i++){
scanf("%d",&n);
if(n!=0){
for(int j=0;j<n;j++){
scanf("%d",&m);
a[i].push_back(m);
}
}
}
DFS(0,1);
double sum = pow(1+r,minDepth-1)*P;
printf("%.4f %d",sum,minNum);
}
A1004
A1094稍加修改即可。
#include<iostream>
#include<vector>
using namespace std;
const int maxn = 110;
vector<int> a[maxn];
int p[maxn]={0},N,M,maxP=0,maxL;
void DFS(int root,int level){
if(a[root].empty())
p[level]++;
for(int i=0;i<a[root].size();i++){
DFS(a[root][i],level+1);
}
if(maxL<level){
maxL = level;
}
}
int main(){
scanf("%d%d",&N,&M);
int n,m,k;
for(int i=0;i<M;i++){
scanf("%d%d",&n,&m);
for(int j=0;j<m;j++){
scanf("%d",&k);
a[n].push_back(k);
}
}
DFS(1,1);
for(int i=1;i<=maxL;i++){
if(i!=1)
printf(" ");
printf("%d",p[i]);
}
return 0;
}
A1043【hard】
问题:给出镜像BST的概念,并给出一个先序序列,要求判断这是否为一个BST或者镜像BST的先序序列,如果是,则还需要输出这个数的后序序列
思路:
1.模拟建树,根据题目给的先序序列建立一个BST,然后求这个BST的先序序列以及镜像先序序列,然后与题目给的序列比较来判断。
2.分析这个先序序列,按照BST的性质去求后序,如果长度不够则不是BST
关键:
#include<cstdio>
#include<vector>
using namespace std;
struct node{
int data;
node* lchild;
node* rchild;
};
vector<int> pre,preM,post,postM,value;
const int maxn = 1010;
void insert(node* &root,int data){
if(root==NULL){
root = new node;
root->data = data;
root->lchild = root->rchild = NULL;
return;
}
else if(data<root->data){
insert(root->lchild,data);
}else{
insert(root->rchild,data);
}
}
void preOrder(node* root){
if(root==NULL)
return;
pre.push_back(root->data);
preOrder(root->lchild);
preOrder(root->rchild);
}
void preOrderM(node* root){
if(root==NULL)
return;
preM.push_back(root->data);
preOrderM(root->rchild);
preOrderM(root->lchild);
}
void postOrder(node* root){
if(root==NULL)
return;
postOrder(root->lchild);
postOrder(root->rchild);
post.push_back(root->data);
}
void postOrderM(node* root){
if(root==NULL)
return;
postOrderM(root->rchild);
postOrderM(root->lchild);
postM.push_back(root->data);
}
int main(){
int N,m;
node* root = NULL;
scanf("%d",&N);
for(int i=0;i<N;i++){
scanf("%d",&m);
value.push_back(m);
insert(root,m);
}
preOrder(root);
preOrderM(root);
if(value==pre){
postOrder(root);
printf("YES\n");
for(int i=0;i<post.size();i++){
if(i!=0)
printf(" ");
printf("%d",post[i]);
}
}else if(value==preM){
postOrderM(root);
printf("YES\n");
for(int i=0;i<postM.size();i++){
if(i!=0)
printf(" ");
printf("%d",postM[i]);
}
}else{
printf("NO");
}
return 0;
}```
## 柳神
```cpp
#include <cstdio>
#include <vector>
using namespace std;
bool isMirror;
vector<int> pre, post;
void getpost(int root, int tail) {
if(root > tail) return ;
int i = root + 1, j = tail;
if(!isMirror) {
while(i <= tail && pre[root] > pre[i]) i++;
while(j > root && pre[root] <= pre[j]) j--;
} else {
while(i <= tail && pre[root] <= pre[i]) i++;
while(j > root && pre[root] > pre[j]) j--;
}
if(i - j != 1) return ;
getpost(root + 1, j);
getpost(i, tail);
post.push_back(pre[root]);
}
int main() {
int n;
scanf("%d", &n);
pre.resize(n);
for(int i = 0; i < n; i++)
scanf("%d", &pre[i]);
getpost(0, n - 1);
if(post.size() != n) {
isMirror = true;
post.clear();
getpost(0, n - 1);
}
if(post.size() == n) {
printf("YES\n%d", post[0]);
for(int i = 1; i < n; i++)
printf(" %d", post[i]);
} else {
printf("NO");
}
return 0;
}
A1064
问题:给出一个CBST(完全二叉查找树)的中序序列,要求求出其层序序列。
思路:可以建立一个数组用来存放完全二叉树。a是当前结点,则
2
a
2a
2a是左子树根结点,
2
a
+
1
2a+1
2a+1是右子树根结点(若存在)。在对完全二叉树遍历时,用题目给的序列依次即可得到unique的CBST。
关键:完全二叉树数组本身从1到n遍历一遍得到的就是层序序列。题目给的序列不一定是从小到大排好序的,需要sort一下,sort默认升序。
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn = 1010;
int a[maxn],b[maxn],index_b=1,n;
vector<int> v;
void InOrder(int root){
if(root>n)
return;
InOrder(root*2);
a[root] = b[index_b++];
InOrder(root*2+1);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&b[i]);
sort(b+1,b+n+1);
InOrder(1);
for(int i=1;i<=n;i++){
if(i!=1)
printf(" ");
printf("%d",a[i]);
}
return 0;
}
A1099
问题:给定BST的结构以及n个不同的数,要求求出这颗unique的BST的层序序列
思路:用结构体数组建树,先序遍历一遍把值赋上,然后再用queue求层序序列即可。
关键:对于题目给的n个不同的数要sort。输出结果部分可以在LevelOrder时一并完成,用一个num记录输出了多少个数,最后一个数不要输出空格即可。
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int maxn = 110;
struct node{
int data;
int lchild;
int rchild;
}a[maxn];
int n,b[maxn],index=0;
void InOrder(int root){
if(root==-1)
return;
InOrder(a[root].lchild);
a[root].data = b[index++];
InOrder(a[root].rchild);
}
vector<int> v;
void LevelOrder(int root){
queue<int> q;
q.push(root);
while(!q.empty()){
int now = q.front();
v.push_back(a[now].data);
q.pop();
if(a[now].lchild!=-1)
q.push(a[now].lchild);
if(a[now].rchild!=-1)
q.push(a[now].rchild);
}
}
int main(){
scanf("%d",&n);
int l,r;
for(int i=0;i<n;i++){
scanf("%d%d",&l,&r);
a[i].lchild = l;
a[i].rchild = r;
}
for(int i=0;i<n;i++)
scanf("%d",&b[i]);
sort(b,b+n);
InOrder(0);
LevelOrder(0);
for(int i=0;i<v.size();i++){
if(i!=0)
printf(" ");
printf("%d",v[i]);
}
return 0;
}
A1127
问题:给出中序序列和后序序列要求给出这个树的Zigzagging Order Sequence
思路:根据中序序列和后序序列建树,用两个栈倒来倒去即可。
关键:
1.考察会不会用中序序列和后序序列建树
2.考察对栈的理解
3.这里用两个栈来互相倒即可
4.这里两个栈的循环可以自己设定一个循环次数,如下代码所示
ac代码:
#include<cstdio>
#include<stack>
#include<vector>
using namespace std;
const int maxn = 40;
int N,in[maxn],post[maxn],index=0;
struct node {
int data,level;
node* lchild;
node* rchild;
};
node* dfs(int inL,int inR,int postL,int postR){
if(postL>postR)
return NULL;
node* root = new node;
root->data = post[postR];
int k = inL;
while(k<=inR&&in[k]!=root->data)k++;
int numL = k-inL;
root->lchild = dfs(inL,k-1,postL,postL+numL-1);
root->rchild = dfs(k+1,inR,postL+numL,postR-1);
return root;
}
void dfs2(node* root,int level){
if(root==NULL)
return;
root->level = level;
dfs2(root->lchild,level+1);
dfs2(root->rchild,level+1);
}
int main(){
scanf("%d",&N);
for(int i=0;i<N;i++)
scanf("%d",&in[i]);
for(int i=0;i<N;i++)
scanf("%d",&post[i]);
node* root = dfs(0,N-1,0,N-1);
dfs2(root,1);
stack<node*> s1;//开两个栈,如果只用一个栈会造成最近压入的结果立刻输出
stack<node*> s2;
vector<node*> v;
s1.push(root);
int skr = 100;
while(skr-->0){//可以直接暴力设定一个循环范围
while(!s1.empty()){
node* now = s1.top();
v.push_back(now);
s1.pop();
if(now->rchild!=NULL)s2.push(now->rchild);
if(now->lchild!=NULL)s2.push(now->lchild);
}
while(!s2.empty()){
node* now = s2.top();
v.push_back(now);
s2.pop();
if(now->lchild!=NULL)s1.push(now->lchild);
if(now->rchild!=NULL)s1.push(now->rchild);
}
}
for(int i=0;i<v.size();i++){
if(i!=0)
printf(" ");
printf("%d",v[i]->data);
}
return 0;
}
也可以判断一下,当栈s2 push完后,s1为空时显然应该break
while(true){
while(!s1.empty()){
node* now = s1.top();
v.push_back(now);
s1.pop();
if(now->rchild!=NULL)s2.push(now->rchild);
if(now->lchild!=NULL)s2.push(now->lchild);
}
while(!s2.empty()){
node* now = s2.top();
v.push_back(now);
s2.pop();
if(now->lchild!=NULL)s1.push(now->lchild);
if(now->rchild!=NULL)s1.push(now->rchild);
}
if(s1.empty())
break;
}
A1115【注意审题!】
问题:给出一组序列,按序插入BST中,然后求这个BST最深两层的结点数。
思路:常规建BST。遍历一遍统计出每层的结点数
关键:
注意审题,这里题目中说BST结点左边是小于或等于,右边是大于。与常规想法不同,所以审题很重要!
#include<cstdio>
using namespace std;
const int maxn = 1010;
int N;
struct node{
int data,level;
node* lchild;
node* rchild;
};
void insert(node* &root,int data){
if(root==NULL){
root = new node;
root->data = data;
root->lchild=root->rchild=NULL;
return;
}
if(data<=root->data)
insert(root->lchild,data);
else if(data>root->data)
insert(root->rchild,data);
}
int maxL = 0;
void dfs(node* root,int level){
if(root==NULL)
return;
if(maxL<level)
maxL = level;
root->level = level;
dfs(root->lchild,level+1);
dfs(root->rchild,level+1);
}
int n1=0,n2=0;
void find(node* root){
if(root->level==maxL)
n1++;
if(root->level==(maxL-1))
n2++;
if(root->lchild!=NULL)
find(root->lchild);
if(root->rchild!=NULL)
find(root->rchild);
}
int main(){
scanf("%d",&N);
node* root = NULL;
int m;
for(int i=0;i<N;i++){
scanf("%d",&m);
insert(root,m);
}
dfs(root,1);
find(root);
printf("%d + %d = %d",n1,n2,n1+n2);
return 0;
}
A1138
问题:题目给出一个树的先序序列和中序序列,要求求出后序序列的第一个数
思路:老老实实求就完事了。
关键:
1.要求后序序列的第一个,那么老实点求后序序列即可,不用搞什么一步到位花里胡哨。
#include<cstdio>
#include<vector>
using namespace std;
const int maxn = 50010;
struct node{
int data;
node* lchild;
node* rchild;
};
int pre[maxn],in[maxn],N;
node* dfs(int preL,int preR,int inL,int inR){
if(inL>inR)
return NULL;
node* root = new node;
root->data = pre[preL];
int k = inL;
while(k<=inR&&in[k]!=root->data)k++;
int numL = k - inL;
root->lchild = dfs(preL+1,preL+numL,inL,k-1);
root->rchild = dfs(preL+numL+1,preR,k+1,inR);
return root;
}
vector<int> v;
void postOrder(node* root){
if(root==NULL)
return;
postOrder(root->lchild);
postOrder(root->rchild);
v.push_back(root->data);
}
int main(){
scanf("%d",&N);
for(int i=0;i<N;i++)
scanf("%d",&pre[i]);
for(int i=0;i<N;i++)
scanf("%d",&in[i]);
node* root = dfs(0,N-1,0,N-1);
while(root->lchild!=NULL)
root = root->lchild;
postOrder(root);
printf("%d",v[0]);
return 0;
}
A1110【hard | review:dfs部分要细品】
问题:判断一棵树是否为完全二叉树
思路:新建一个数组,用1表示根结点,如果k是根,那么
2
k
2k
2k存左子节点,
2
k
+
1
2k+1
2k+1存右子节点,然后判断这个数组的有效大小是否为总个数-1即可。
关键:
1.这里要用string去读,如果用char那么只能处理一位数的情况,很容易出错误。
2.string的"=="比较很好用,stoi函数很好用
3.上述思路并需要真的用一个数组去存这些点,只需要点的原下标与我们设定的这个数组的下标对应上即可。
#include<iostream>
// #include<string>
using namespace std;
const int maxn = 30;
int N,book[maxn],maxIndex=-1,maxRoot;
struct node{
int lchild;
int rchild;
}a[maxn];
void dfs(int root,int index){
if(root==-1)
return;
if(maxIndex<index){
maxIndex = index;
maxRoot = root;
}
dfs(a[root].lchild,index*2);
dfs(a[root].rchild,index*2+1);
}
int main(){
cin >> N;
string s1,s2;
for(int i=0;i<N;i++){
cin >> s1 >> s2;
if(s1=="-"){
a[i].lchild = -1;
}else{
a[i].lchild = stoi(s1);
book[stoi(s1)] = 1;
}
if(s2=="-"){
a[i].rchild = -1;
}else{
a[i].rchild = stoi(s2);
book[stoi(s2)] = 1;
}
}
int root = 0;
for(;root<N;root++){
if(book[root]!=1)
break;
}
dfs(root,1);
if(maxIndex==N){
printf("YES %d",maxRoot);
}else{
printf("NO %d",root);
}
}
A1143【disgusting | 防一手】
问题:给一个BST的先序序列,求LCA(Lowest Common Ancestor)
思路:用一个map去记录有效结点,不需要建树因为BST的根的值肯定在它的子结点(两个要求的点)的值之间,或者根就是其中一个要求的点。
关键:
被这题的样本点真的恶心坏了,如果用数组去记录各个输入的点,无论开的多大都会有一个点通不过。
必须得用map<int,bool>去做才能全部通过,醉了(反正如果真在考试的时候我是想不到会在这种地方出问题)
防一手 → \rightarrow → 在做记录时常用map,少用数组,切记切记。
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
int M,N;
map<int,bool> book;
int main(){
int m,u,v,a;
scanf("%d %d",&M,&N);
vector<int> pre;
for(int i=0;i<N;i++){
scanf("%d",&m);
pre.push_back(m);
book[m]=true;
}
for(int i=0;i<M;i++){
scanf("%d %d",&u,&v);
if(!book[u]&&!book[v]){
printf("ERROR: %d and %d are not found.\n",u,v);
continue;
}
else if(!book[u]||!book[v]){
printf("ERROR: %d is not found.\n",book[u]?v:u);
continue;
}
for(int j=0;j<pre.size();j++){
a = pre[j];
if((a>u&&a<v)||(a>v&&a<u)||(a==u)||(a==v))
break;
}
if(a==u||a==v)
printf("%d is an ancestor of %d.\n",a,(a==u)?v:u);
else
printf("LCA of %d and %d is %d.\n",u,v,a);
}
return 0;
}
AVL
A1066【hard】
问题:题目给出AVL树各个结点值的插入顺序,要求求出该树的根的值为多少。
思路:按常规的做就行,主要看有没有复习到AVL。
关键:直接输出中位数可以嫖到不少分。
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 30;
int n,a[maxn];
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
sort(a,a+n);
printf("%d",a[n/2]);
}
一发AC
#include<cstdio>
#include<algorithm>
using namespace std;
struct node{
int data;
int height;
node* lchild;
node* rchild;
};
const int maxn = 30;
int n,a[maxn];
node* create(int data){
node* root = new node;
root->data = data;
root->height = 1;
root->lchild = NULL;
root->rchild = NULL;
return root;
}
int getHeight(node* root){
if(root==NULL)
return 0;
return root->height;
}
void UpdateHeight(node* &root){
root->height = max(getHeight(root->lchild),getHeight(root->rchild)) + 1;
}
int getBF(node* root){
return getHeight(root->lchild) - getHeight(root->rchild);
}
void L(node* &root){
node* temp = root->rchild;
root->rchild = temp->lchild;
temp->lchild = root;
UpdateHeight(root);
UpdateHeight(temp);
root = temp;
}
void R(node* &root){
node* temp = root->lchild;
root->lchild = temp->rchild;
temp->rchild = root;
UpdateHeight(root);
UpdateHeight(temp);
root = temp;
}
void insert(node* &root,int data){
if(root==NULL){
root = create(data);
return;
}
if(data < root->data){
insert(root->lchild,data);
UpdateHeight(root);
if(getBF(root)==2){
if(getBF(root->lchild)==1){//LL
R(root);
}else if(getBF(root->lchild)==-1){//LR
L(root->lchild);
R(root);
}
}
}else if(data > root->data){
insert(root->rchild,data);
UpdateHeight(root);
if(getBF(root)==-2){
if(getBF(root->rchild)==1){//RL
R(root->rchild);
L(root);
}else if(getBF(root->rchild)==-1){//RR
L(root);
}
}
}
}
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
node* root = create(a[0]);
for(int i=1;i<n;i++)
insert(root,a[i]);
printf("%d",root->data);
return 0;
}
并查集
路径压缩的递归式的findFather
int findFather(int v){
if(v==father[v])
return v;
else{
int f = findFather(father[v]);//为了让递归动起来所以这里需要father[v]
father[v] = f;
return f;
}
}
A1107【hard】
问题:n个人有自己不同的hobby,有相同hobby的人是一类,根据题目给出的hobby情况求出这些人可以分为几类
思路:创建一个hobby[k],k表示hobby的index,hobby[k]表示这个hobby的代表者,如果其他人也有类似的hobby k,则将这个人与代表者Union起来。这样我们就得到了并查集father[],然后根据并查集进行统计,这部分是有固定格式的:
int group[n] = {0};
for(int k=1;k<n;k++)
group[father[k]]++;
关键:
1.数组开大点没事的,反正操作都不是很复杂
2.将数组的下标赋予意义
3.sort 降序排序
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 1010;
int N,father[maxn],course[maxn]={0},group[maxn]={0};
void init(int n){
for(int i=1;i<=n;i++){
father[i] = i;
}
}
int findFather(int a){
if(a==father[a])
return a;
int F = findFather(father[a]);
father[a] = F;
return F;
}
void Union(int a,int b){
int f1 = findFather(a);
int f2 = findFather(b);
if(f1!=f2)
father[f1] = f2;
}
bool cmp(int a,int b){
return a>b;
}
int main(){
scanf("%d",&N);
init(N);
int n,m;
for(int i=1;i<=N;i++){
scanf("%d: ",&n);
for(int j=0;j<n;j++){
scanf("%d",&m);
if(course[m]==0)
course[m] = i;
else{
Union(i,course[m]);
}
}
}
for(int i=1;i<=N;i++){
group[findFather(i)]++;
}
int num=0;
for(int i=1;i<=N;i++){
if(group[i]!=0)
num++;
}
sort(group+1,group+N+1,cmp);
printf("%d\n%d",num,group[1]);
for(int i=2;i<=num;i++){
printf(" %d",group[i]);
}
return 0;
}
A1118
和A1107类似,开一个course[]用于记录一张照片的代表者。得到并查集father[]后开一个book记录一下father的bool情况即可。最后判断两个鸟是否是同一个树用一个findFather==findFather判断即可。
#include<cstdio>
using namespace std;
const int maxn = 10010;
int N,maxNum=0,father[maxn]={0},course[maxn]={0},book[maxn]={0};
int findFather(int a){
if(a==father[a])
return a;
int F = findFather(father[a]);
father[a] = F;
return F;
}
void init(){
for(int i=0;i<maxn;i++)
father[i] = i;
}
void Union(int a,int b){
int f1 = findFather(a);
int f2 = findFather(b);
father[f1] = f2;
}
int main(){
init();
int n,m;
scanf("%d",&N);
for(int i=0;i<N;i++){
scanf("%d",&n);
for(int j=0;j<n;j++){
scanf("%d",&m);
if(course[i]==0)
course[i]=m;
else{
Union(m,course[i]);
}
if(m>maxNum)
maxNum = m;
}
}
for(int i=1;i<=maxNum;i++){
book[findFather(i)] = 1;
}
int sum = 0;
for(int i=1;i<=maxNum;i++){
sum+=book[i];
}
printf("%d %d\n",sum,maxNum);
int _n;
scanf("%d",&_n);
int _a,_b;
for(int i=0;i<_n;i++){
scanf("%d %d",&_a,&_b);
if(findFather(_a)==findFather(_b))
printf("Yes\n");
else
printf("No\n");
}
return 0;
}
A1034【hard | review】
问题:根据多个通话记录判断有几个团伙以及他们的老大和人数。
思路:并查集,也可以用图论DFS去做
关键:未ac 看了其他人的代码发现自己好像理解错误了,题目中说人的姓名由A-Z的三个大写字母组成,我理解为AAA一直到ZZZ就这么2个人了 /wul。所以这种一一对应的还是要用map,不要取巧。
用并查集的代码:
#include<iostream>
#include<map>
#include<vector>
using namespace std;
const int maxn = 2010;
map<string,int> stringToint;
map<int,string> intTostring;
int N,K,father[maxn],head[maxn]={0},weight[maxn]={0},total[maxn]={0},peopleNumber[maxn]={0},book[maxn]={0};
void init(){
for(int i=0;i<maxn;i++)
father[i] = i;
}
int find(int x){
if(x==father[x])
return x;
int f = find(father[x]);
father[x] = f;
return f;
}
void Union(int x,int y){
father[find(x)] = find(y);
}
int idNumber = 1;
int stoifunc(string str){
if(stringToint[str]==0){
stringToint[str] = idNumber;
intTostring[idNumber] = str;
return idNumber++;
}else{
return stringToint[str];
}
}
int main(){
// freopen("1.txt","r",stdin);
init();
scanf("%d %d",&N,&K);
string str_a,str_b;
int w;
for(int i=0;i<N;i++){
cin >> str_a >> str_b >> w;
int id1 = stoifunc(str_a);
int id2 = stoifunc(str_b);
weight[id1]+=w;
weight[id2]+=w;
Union(id1,id2);
}
//统计gang的总人数和总时长
for(int i=1;i<idNumber;i++){
peopleNumber[find(i)]++;
total[find(i)]+=weight[i];
//统计gang中的head
if(head[find(i)]==0)
head[find(i)] = i;
if(weight[i]>weight[head[find(i)]]){
head[find(i)] = i;
}
}
map<string,int> gang;
for(int i=1;i<=idNumber;i++){
if(peopleNumber[i]>2&&(total[i]/2)>K)
gang[intTostring[head[i]]] = peopleNumber[i];
}
cout << gang.size() << endl;
map<string,int>::iterator it;
for(it=gang.begin();it!=gang.end();it++){
cout << it->first << " " << it->second << endl;
}
return 0;
}
用图论、DFS的代码:
#include<iostream>
#include<map>
#include<vector>
using namespace std;
const int maxn = 2010;
map<string,int> stringToint;
map<int,string> intTostring;
int N,K,G[maxn][maxn],vis[maxn],weight[maxn];
int idNumber = 1;
int stoifunc(string str){
if(stringToint[str]==0){
stringToint[str] = idNumber;
intTostring[idNumber] = str;
return idNumber++;
}else{
return stringToint[str];
}
}
void dfs(int root,int &head,int &peopleNumber,int &total){
vis[root] = 1;
peopleNumber++;
if(weight[root] > weight[head])
head = root;
for(int i=1;i<idNumber;i++){
if(G[root][i] > 0){//对于那些还没访问过边的(无论是否vis因为可能有环)
total += G[root][i];
G[root][i] = 0;//归零 防止重复访问
G[i][root] = 0;
if(!vis[i])
dfs(i,head,peopleNumber,total);
}
}
}
int main(){
scanf("%d %d",&N,&K);
string str_a,str_b;
int w;
for(int i=0;i<N;i++){
cin >> str_a >> str_b >> w;
int id1 = stoifunc(str_a);
int id2 = stoifunc(str_b);
weight[id1]+=w;
weight[id2]+=w;
G[id1][id2] += w;
G[id2][id1] += w;
}
map<string,int> gang;
int peopleNumber,total,head;
for(int i=1;i<idNumber;i++){
if(!vis[i]){
head = i;
peopleNumber = 0;
total = 0;
dfs(i,head,peopleNumber,total);//访问i所在的连通块
if(peopleNumber>2&&total>K)
gang[intTostring[head]] = peopleNumber;
}
}
cout << gang.size() << endl;
for(auto it=gang.begin();it!=gang.end();it++){
cout << it->first << " " << it->second << endl;
}
}
A1114【hard | complex | review】
问题:给了一堆关于家庭人口、房产、房产面积的信息,根据要求输出
思路:结构体数组+并查集
关键:
1.对于种类繁杂的信息要学会用结构体去存储
2.对于房产数和房产面积的处理要在并查集建立完毕后再处理,因为在建立的过程中每个家族的father根结点会不断移动。
3.因为题目中要求家族最小的id,所以在Union时可以做一个判断,将更小的id作为father【细品】
4.统计各个家族的总人数时应该要用全部遍历一遍的方式去做,如果一个结构体一个结构体的找中间一些重复的人会被计入。
AC代码:
#include<cstdio>
#include<algorithm>
#include<set>
#include<vector>
using namespace std;
const int maxn = 10010;
int N,father[maxn],people[maxn];
double estate[maxn],area[maxn];
struct data{
int id,fid,mid,k;
int childid[10];
int estate,area;
bool flag;
};
void init(){
for(int i=0;i<maxn;i++)
father[i] = i;
}
int find(int x){
if(x == father[x])
return x;
int f = find(father[x]);
father[x] = f;
return f;
}
void Union(int x,int y){//题目中要求最小id,因此用更小的id作为father
int f1 = find(x);
int f2 = find(y);
if(f1<f2)
father[f2] = f1;
if(f2<f1)
father[f1] = f2;
}
bool cmp(int x,int y){
if(area[x]==area[y])
return x < y;
return area[x] > area[y];
}
int main(){
scanf("%d",&N);
data a[N];
init();
int k;
for(int i=0;i<N;i++){
scanf("%d %d %d %d",&a[i].id,&a[i].fid,&a[i].mid,&a[i].k);
if(a[i].fid!=-1)
Union(a[i].fid,a[i].id);
if(a[i].mid!=-1)
Union(a[i].mid,a[i].id);
for(int j=0;j<a[i].k;j++){
scanf("%d",&a[i].childid[j]);
Union(a[i].childid[j],a[i].id);
}
scanf("%d %d",&a[i].estate,&a[i].area);
}
set<int> st;
for(int i=0;i<N;i++){
int f = find(a[i].id);
st.insert(f);
estate[f] += (double)a[i].estate;
area[f] += (double)a[i].area;
}
for(int i=0;i<10000;i++){
people[find(i)]++;
}
for(auto f:st){
estate[f] /= people[f];
area[f] /= people[f];
}
vector<int> v;
v.assign(st.begin(),st.end());
sort(v.begin(),v.end(),cmp);
printf("%d\n",v.size());
for(int i=0;i<v.size();i++){
int id = v[i];
printf("%04d %d %.3f %.3f\n",id,people[id],estate[id],area[id]);
}
return 0;
}
堆
A1098【hard】
问题:给出一个初始序列,一个部分排好序的序列,要求判断这个序列是InsertionSort or heapSort并输出该排序模式的下一步序列结果。
思路:
1、两种排序都试一遍,然后比较得出是哪一种排序,然后输出下一步的序列结果。
2、有分析可知,插入排序的前几个都是有序的;堆排序的后几个都是有序的,可以根据这个特性来判断。
关键:
1、插入排序的部分如果要模拟,可以直接用sort。
2、这道题中说的是中间序列“ partially sorted sequence”,所以初始序列是不考虑的。如下例所示:
//input
4
3 4 2 1
3 4 2 1
//output
Insertion Sort
2 3 4 1
3、对于函数参数写法:
func(int* a)与func(int a[])是等价的,都是以指针参数,而不是将整个数组复制一遍。
4、在模拟时,如果从初始序列开始模拟,那么HeapSort需要先建堆,即对
N
2
−
1
\frac{N}{2} - 1
2N−1都要做一次调整。
5、头文件中的std::swap可以直接调用,swap可以理解是通过指针交换的,但是不需要取地址。
通过模拟的方式ac的代码:
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 110;
int N,a[maxn],target[maxn];
bool isSame(int a[],int b[]){
for(int i=1;i<=N;i++){
if(a[i]!=b[i])
return false;
}
return true;
}
void showArray(int a[]){
for(int i=1;i<=N;i++){
if(i!=1)
printf(" ");
printf("%d",a[i]);
}
}
void InsertionSort(int origin[]){
bool flag = false;
for(int i=2;i<=N;i++){
if(i!=2&&isSame(origin,target)){
flag = true;
}
sort(origin+1,origin+i+1);
if(flag){
printf("Insertion Sort\n");
showArray(origin);
return;
}
}
}
void swap(int *a,int *b){
int c = *a;
*a = *b;
*b = c;
}
void DownAdjust(int* origin,int low,int high){
int i = low, j = 2*i;
while(j<=high){
if(j+1<=high&&origin[j+1]>origin[j])
j++;
if(origin[i]<origin[j]){
swap(&origin[i],&origin[j]);
i = j;
j = 2*i;
}else{
break;
}
}
}
void HeapSort(int origin[]){
bool flag = false;
//先建堆
for(int i=N/2;i>=1;i--)
DownAdjust(origin,i,N);
for(int i=N;i>1;i--){
//i!=N以保证是中间过程
if(i!=N&&isSame(origin,target)){
flag = true;
}
swap(origin+1,origin+i);
DownAdjust(origin,1,i-1);
if(flag){
printf("Heap Sort\n");
showArray(origin);
return;
}
}
}
int main(){
scanf("%d",&N);
for(int i=1;i<=N;i++)
scanf("%d",&a[i]);
for(int i=1;i<=N;i++)
scanf("%d",&target[i]);
int b[maxn];
for(int i=1;i<=N;i++)
b[i] = a[i];
InsertionSort(a);
HeapSort(b);
return 0;
}
通过特性来判断的ac的代码:
这里online judge 时有两处通不过:
1.对于下标k和p退出循环后的位置不够清晰
2.这里越过前面的有序子序列时,条件应该为b[k]<=b[k+1],挺坑的,但是实际上很好理解,因为这道题中初始序列是不考虑的,下举例子说明:
6
3 3 2 1 0 4 a[]
2 3 3 1 0 4 b[]
//output
1 2 3 3 0 4
如果不越过相邻相等的情况并且这是一个Insertion Sort 那么输出的结果和b一样,显然是错误的。
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 110;
int N,a[maxn],b[maxn];
void downAdjust(int low,int high){
int i=low,j=i*2;
while(j<=high){
if(j+1<=high&&b[j+1]>b[j])
j++;
if(b[i]<b[j]){
swap(b[i],b[j]);
i = j;
j = i*2;
}else{
break;
}
}
}
int main(){
scanf("%d",&N);
for(int i=1;i<=N;i++)
scanf("%d",&a[i]);
for(int i=1;i<=N;i++)
scanf("%d",&b[i]);
int k = 1;
while(k<=N&&b[k]<=b[k+1])//如果是Insertion,那么越过前面有序子序列
k++;
k++;
int index = k;
while(k<=N&&a[k]==b[k])//如果是Insertion,那么后面子序列应该没有变动
k++;
if(k==(N+1)){
printf("Insertion Sort\n");
sort(b+1,b+index+1);
}else{
printf("Heap Sort\n");
/*
说明是HeapSort
这里的b已经是一个HeapSort的中间过程
*/
int p = N;
while(p>=1&&b[p-1]<b[p])
p--;
swap(b[1],b[p]);
downAdjust(1,p-1);
}
for(int i=1;i<=N;i++){
if(i!=1)
printf(" ");
printf("%d",b[i]);
}
}
哈夫曼树
priority_queue的用法
//升序队列
priority_queue <int,vector<int>,greater<int> > q;
//降序队列
priority_queue <int,vector<int>,less<int> >q;
//对于基础类型 默认是大顶堆
priority_queue<int> a;
//等同于 priority_queue<int, vector<int>, less<int> > a;
// 这里一定要有空格,不然成了右移运算符↓
priority_queue<int, vector<int>, greater<int> > c; //这样就是小顶堆