7-1 二叉树最长路径 (100 分)
作者 :谷方明
单位 :吉林大学
代码长度限制 :16 KB
时间限制 :100 ms
内存限制 :5 MB
给定一棵二叉树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
- 最开始,我采用了递归建树,在求二叉树的最长长度时,我递归使用了DFS,然后有一个样例运行超时。
#include<bits/stdc++.h>
using namespace std;
struct{
int left;
int right;
}Node[100000];
int maxn=0;
vector<int> now;
vector<int> result;
inline void maketree(int a){
if(a==-1) return;
scanf("%d",&Node[a].left);
maketree(Node[a].left);
scanf("%d",&Node[a].right);
maketree(Node[a].right);
}
inline void dfs(int a,int time){
if(a==-1) return;
now.push_back(a);
if(time>=maxn){
maxn=time;
result=now;
}
dfs(Node[a].left,time+1);
dfs(Node[a].right,time+1);
now.pop_back();
}
int main(){
int n;
scanf("%d",&n);
int root;
scanf("%d",&root);
maketree(root);
dfs(root,0);
printf("%d\n",maxn);
for(int i=0;i<result.size();i++){
if(i) printf(" ");
printf("%d",result[i]);
}
printf("\n");
return 0;
}
后来又改了一下,在构建树的时候就把最长长度求出来,结果还是那一个样例超时,我觉得应该是result更新的次数过多的原因。
#include<bits/stdc++.h>
using namespace std;
struct{
int left;
int right;
}Node[100005];
int maxn=0;
vector<int>now;
vector<int>result;
void maketree(int a,int time){
if(a==-1) {
if(time-1>=maxn){
maxn=time-1;
result=now;
}
return;
}
now.push_back(a);
scanf("%d",&Node[a].left);
maketree(Node[a].left,time+1);
scanf("%d",&Node[a].right);
maketree(Node[a].right,time+1);
now.pop_back();
}
int main(){
int n;
scanf("%d",&n);
int root;
scanf("%d",&root);
maketree(root,0);
printf("%d\n",maxn);
for(int i=0;i<=maxn;i++){
if(i) printf(" ");
printf("%d",result[i]);
}
printf("\n");
return 0;
}
最后改为记录每个结点的父亲,每次只更新叶子节点,更新的代价就变为O(1)了
#include<bits/stdc++.h>
using namespace std;
struct {
int parent;
int left;
int right;
} Node[100005];
int maxn=0;
int mnode=0;
int now[100005];
int result[100005];
void maketree(int p,int a,int time) {
if(a==-1) {
//cout<<p<<" "<<time-1<<" "<<maxn<<" "<<mnode<<endl;
if(time-1>=maxn) {
maxn=time-1;
mnode=p;
}
return;
}
now[time]=a;
//cout<<a<<" "<<mnode<<endl;
scanf("%d",&Node[a].left);
if(Node[a].left!=-1) Node[Node[a].left].parent=a;
maketree(a,Node[a].left,time+1);
scanf("%d",&Node[a].right);
if(Node[a].right!=-1) Node[Node[a].right].parent=a;
maketree(a,Node[a].right,time+1);
}
int main() {
int n;
scanf("%d",&n);
int root;
scanf("%d",&root);
maketree(0,root,0);
int m=maxn;
result[m]=mnode;
while(m) {
result[m-1]=Node[result[m]].parent;
m--;
}
printf("%d\n",maxn);
for(int i=0; i<=maxn; i++) {
if(i) printf(" ");
printf("%d",result[i]);
}
printf("\n");
return 0;
}
7-2 森林的层次遍历 (100 分)
作者: 谷方明
单位 :吉林大学
代码长度限制 :16 KB
时间限制 :100 ms
内存限制 :5 MB
给定一个森林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
- 当时在考试的时候,程序测试就显示内存超限,当时超没自信觉得这道题我写不出来了,连改也没改就去做三四题了,今天一改发现原因是少写了一个取地址符。在软件工程专业学习一年了,还会犯这种错误,我也是对自己无话可说了。当然,改完之后,还是没有过,第二个点内存超限(我是菜逼实锤)。
- 这一题的思路和上一题的思路几乎完全一样,使用递归建树,但是由于这道题是一个森林,所以引入了c,记录参与建树的节点数目。用vector储存每个结点的子节点们,在层次遍历的时候则使用了队列来作为辅助结构。
下面是内存超限的代码。
#include<bits/stdc++.h>
using namespace std;
queue<int> p;
struct {
char c;
int d;
} Node[100000];
vector<vector<int> >q;
int c=1;
void makeforest(int a,int d) {
c++;
//cout<<a<<" "<<"建树测试点"<<endl;
int count=1;
int n=a;
while(count<=d) {
//cout<<"循环测试"<<endl;
q[a].push_back(c);
scanf("%d",&Node[c].d);
//cout<<c<<" "<<"测试点二 "<<Node[c].d<<endl;
makeforest(c,Node[c].d);
count++;
}
}
int main() {
int n;
scanf("%d",&n);
q.resize(n+1);
while(!p.empty()) p.pop();
int m;
for(int i=1; i<=n; i++) {
cin>>Node[i].c;
}
scanf("%d",&Node[1].d);
p.push(1);
//cout<<"测试点1"<<endl;
makeforest(1,Node[1].d);
//cout<<"测试点2"<<endl;
while(c<=n) {
p.push(c);
scanf("%d",&Node[c].d);
makeforest(c,Node[c].d);
}
//cout<<"测试点3"<<endl;
while(!p.empty()) {
printf("%c",Node[p.front()].c);
for(int i=0; i<Node[p.front()].d; i++) {
p.push(q[p.front()][i]);
}
p.pop();
printf("%c",p.empty()?'\n':' ');
}
return 0;
}
- 把结构体数组删掉了,然后用了一个字符型数组,一个整型数组,就过了!!!
#include<bits/stdc++.h> using namespace std; const int MAXN=1e5+5; queue<int> p; int n; char ch[MAXN]; int du[MAXN];//记录结点的度数 vector<vector<int> >q; int c=1; void makeforest(int a,int d) { c++; //cout<<a<<" "<<"建树测试点"<<endl; int count=1; int n=a; while(count<=d) { //cout<<"循环测试"<<endl; q[a].push_back(c); scanf("%d",&du[c]); //cout<<c<<" "<<"测试点二 "<<Node[c].d<<endl; makeforest(c,du[c]); count++; } } int main() { scanf("%d",&n); q.resize(n+1); while(!p.empty()) p.pop(); int m; for(int i=1; i<=n; i++) { cin>>ch[i]; } scanf("%d",&du[1]); p.push(1); //cout<<"测试点1"<<endl; makeforest(1,du[1]); //cout<<"测试点2"<<endl; while(c<=n) { p.push(c); scanf("%d",&du[c]); makeforest(c,du[c]); } //cout<<"测试点3"<<endl; while(!p.empty()) { printf("%c",ch[p.front()]); for(int i=0; i<du[p.front()]; i++) { p.push(q[p.front()][i]); } p.pop(); printf("%c",p.empty()?'\n':' '); } return 0; }
7-3 纸带切割 (100 分)
作者: 谷方明
单位: 吉林大学
代码长度限制: 16 KB
时间限制: 100 ms
内存限制: 5 MB
有一条细长的纸带,长度为 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
- 这道题就是每次取最小的两个相加,并记录下来就可以了,逆着题干思考就好。
- 后来班里的大佬说是这是哈夫曼树,我还是没有反应过来。
- 这道题一开始只过了一个点,第一个点报的是格式错误,第三第四个点报的是答案错误,我其实不太会用优先队列的,每次都会手动sort(第四题就是因为这个原因超时了),后来使用了优先队列错误也没有消失。然后,我把int改成了long long过了第三四个点,第一个点应该是n=1的测试点,这时总代价为0,第二行应该是没有输出的,我一开始输出了一个零结果错了。
#include<bits/stdc++.h> using namespace std; priority_queue<long long,vector<long long>,greater<long long> >q; vector<long long>p; long long sum=0; int main(){ int n; scanf("%d",&n); for(int i=0;i<n;i++){ long long a; scanf("%lld",&a); q.push(a); } int m=n; while(m-1){ int a,b; a=q.top(); q.pop(); b=q.top(); q.pop(); q.push(a+b); p.push_back(a+b); sum+=a+b; m--; } printf("%lld\n",sum); for(int i=n-2;i>=0;i--){ if(i!=n-2) printf(" "); printf("%lld",p[i]); } if(n!=1) printf("\n"); return 0; }
7-4 序列乘积 (100 分)
作者 :谷方明
单位 :吉林大学
代码长度限制 :16 KB
时间限制 :100 ms
内存限制 :5 MB
两个递增序列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
- 这道题,我一开始没有注意到输入序列递增的特点,所以采取了暴力求解的方式,得了应该是60分。
- 后来听了班里大佬的思路,利用序列递增的特点,将乘积看作一个n*n矩阵,那么一行的最小值一定会在这一行的开头,将开头一列放入堆中,每次输出最小值,并将最小值左边的数放入队中,直到输出n个数。
- 在按照上述思路完成题目时,我发现不仅要保存乘积,还要保存乘积在矩阵中的行数和列数。通过看炫神的代码,我见识到了结构体数组这种厉害的东西。为了实现升序数组,在结构体中对"<"进行了重载,还在结构体中写了类似类的构建函数的函数,为什么可以这么写,我暂时还没有弄明白。
下面是结构体优先队列的代码。
struct Node{
int r,c;//行数和列数
long long data;
bool operator<(const Node& a)const{
return data>a.data;
}
Node(int x,int y,long long d){
r=x; c=y; data=d;
}
Node(){}
};
priority_queue<Node>q;
- 下面是完整代码。
#include<bits/stdc++.h> using namespace std; const int MAXN=1e5+5; int n; int a[MAXN]; int b[MAXN]; struct Node{ int r,c;//行数和列数 long long data; bool operator<(const Node& a)const{ return data>a.data; } Node(int x,int y,long long d){ r=x; c=y; data=d; } Node(){} }; priority_queue<Node>q; int main(){ Node buffer; 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]); for(int i=1;i<=n;i++) q.push(Node(1,i,a[1]*b[i])); for(int i=1;i<=n;i++){ buffer=q.top(); printf("%lld",buffer.data); printf("%c",i==n?'\n':' '); q.pop(); q.push(Node(buffer.r+1,buffer.c,a[buffer.r+1]*b[buffer.c])); } return 0; }