JLU数据结构上机实验三

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
  • 这道题就是每次取最小的两个相加,并记录下来就可以了,逆着题干思考就好。
  • 后来班里的大佬说是这是哈夫曼树,我还是没有反应过来。
  1. 这道题一开始只过了一个点,第一个点报的是格式错误,第三第四个点报的是答案错误,我其实不太会用优先队列的,每次都会手动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;
    }

     

  • 0
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值