title

【JLU数据结构荣誉课】第三次上机实验

->点我进原题
-> 7-1 二叉树最长路径
-> 7-2 森林的层次遍历
-> 7-3 纸带切割
-> 7-4 序列乘积

7-1 二叉树最长路径 (100 分)


代码长度限制 \(16 KB\)
时间限制 \(100 ms\)
内存限制 \(5 MB\)


Description

给定一棵二叉树 \(T\),求 \(T\) 中的最长路径的长度,并输出此路径上各结点的值。若有多条最长路径,输出最右侧的那条。

Input

第1行,\(1\) 个整数 \(n\),表示二叉树有 \(n\) 个结点, \(1≤n≤100000\).

第2行,\(2n+1\) 个整数,用空格分隔,表示T的扩展先根序列, \(-1\) 表示空指针,结点用编号 \(1\)\(n\) 表示。

Output

第1行,\(1\) 个整数 \(length\)\(length\) 表示 \(T\) 中的最长路径的长度。

第2行,\(length+1\) 个整数,用空格分隔,表示最右侧的最长路径。

Sample Input

5
1 2 -1 -1 3 4 -1 -1 5 -1 -1

Sample Output

2
1 3 5

思路

先按照扩展先根序列建树,执行根左右的顺序,遇到-1就返回。对于求其最长路径,直接dfs暴力求解即可,在长度相等时也更新一下,以保证求得的是最右最长序列。

代码

#include<cstdio>
#include<cctype>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#define rg register
#define ll long long
using namespace std;
inline int read(){
	rg int f = 0, x = 0;
	rg char ch = getchar();
	while(!isdigit(ch))	f |= (ch == '-'), ch = getchar();
	while( isdigit(ch))	x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
	return f? -x: x;
}
struct tree{
	int ls, rs;
}t[100010];
int n, root, maxn = 0, ans[100010], temp[100010];
inline int build(){
	int tmp = read();
	if(tmp == -1)	return 0;
	else{
		t[tmp].ls = build();
		t[tmp].rs = build();
	}
	return tmp;
}
inline void dfs(rg int u, rg int tot){
	int lson = t[u].ls, rson = t[u].rs;
	if(!lson && !rson){
		if(tot >= maxn){
			maxn = tot;
			for(rg int i = 0; i <= maxn; ++i)	ans[i] = temp[i];
		} else	return ;
	}
	if(lson){
		temp[++tot] = lson;
		dfs(lson, tot);
		tot --;
	}
	if(rson){
		temp[++tot] = rson;
		dfs(rson, tot);
		tot --;
	}
}
signed main(){
	n = read();
	for(rg int i = 1; i <= n; ++i)	t[i].ls = t[i].rs = 0;
	root = build();
	dfs(root, 0);
	ans[0] = root;
	printf("%d\n", maxn);
	for(rg int i = 0; i <= maxn; ++i){
		printf("%d", ans[i]);
		if(i != maxn)	printf(" ");
	}
	return 0;
}
/*
6
1 2 -1 -1 3 4 -1 6 -1 -1 5 -1 -1
*/




7-2 森林的层次遍历 (100 分)


代码长度限制 \(16 KB\)
时间限制 \(100 ms\)
内存限制 \(5 MB\)


Description

给定一个森林 \(F\),求 \(F\) 的层次遍历序列。森林由其先根序列及序列中每个结点的度给出。

Input

第1行,1个整数 \(n\),表示森林的结点个数, \(1≤n≤100000\).

第2行,\(n\) 个字符,用空格分隔,表示森林 \(F\) 的先根序列。字符为大小写字母及数字。

第3行,\(n\) 个整数,用空格分隔,表示森林 \(F\) 的先根序列中每个结点对应的度。

Output

1行,\(n\) 个字符,用空格分隔,表示森林 \(F\) 的层次遍历序列。

Sample Input

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

Sample Output

A M B C G H N D E F I L J K

思路

题目中给出了度数和先根序列,所以我们建树时,每个节点的度数则是其子节点的个数,由于是先根序列,所以每次对于度数非0的节点,要递归找其子节点、其子节点的节点等等(见build()函数)。起初思路是用非vector版本的邻接表建边,将根节点存下来,再bfs将每次队列中的节点的子节点入队并输出得解,但由于非vector的邻接表在存图时会反着存,所以在层次遍历时要再反过来,栈溢出导致段错误;同时由于边数的不确定也导致了MLE。最后改成用vector存图就AC了。

代码

#include<cstdio>
#include<cctype>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#define rg register
#define ll long long
using namespace std;
inline int read(){
	rg int f = 0, x = 0;
	rg char ch = getchar();
	while(!isdigit(ch))	f |= (ch == '-'), ch = getchar();
	while( isdigit(ch))	x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
	return f? -x: x;
}
int deg[100010];
int n;
char ch[100010][2];
vector<int> e[100010];
int tot = 0;
int cnt = 0;
queue<int> q;
inline int build(){
	int tmp = ++ cnt;
	for(rg int i = 1; i <= deg[tmp]; ++i){
		e[tmp].push_back(build());
	}
	return tmp;
}
signed main(){
	n = read();
	for(rg int i = 0; i < n; ++i)	scanf("%s", ch[i]);
	for(rg int i = 1; i <= n; ++i)	deg[i] = read();
	while(cnt < n){
		int tmp = build();
		q.push(tmp);
	}
	while(!q.empty()){
		int tmp = q.front();
		q.pop();
		cout << ch[tmp - 1];
		for(rg int i = 0; i < deg[tmp]; ++i){
			q.push(e[tmp][i]);
		}
		if(!q.empty())	putchar(' ');
	}
	return 0;
}




7-3 纸带切割 (100 分)


代码长度限制 \(16 KB\)
时间限制 \(100 ms\)
内存限制 \(5 MB\)


Description

有一条细长的纸带,长度为 \(L\) 个单位,宽度为一个单位。现在要将纸带切割成 \(n\) 段。每次切割把当前纸带分成两段,切割位置都在整数单位上,切割代价是当前切割纸带的总长度。每次切割都选择未达最终要求的最长纸带切割,若这样的纸带有多条,则任选一条切割。如何切割,才能完成任务,并且总代价最小。

Input

第1行,\(1\) 个整数 \(n\),表示切割成的段数, \(1≤n≤100000\).

第2行,\(n\) 个整数 \(Li\),用空格分隔,表示要切割成的各段的长度,\(1≤Li≤200000000\)\(1≤i≤n\).

Output

第1行,1个整数,表示最小的总代价。

第2行,若干个整数,用空格分隔,表示总代价最小时每次切割的代价。

Sample Input

5
5 6 7 2 4

Sample Output

54
24 13 11 6

思路

题目说切割纸带,但我们可以反过来将切好的纸带合并成原来的纸带,于是就变成了合并果子(bushi。维护一个小根堆,将所有纸带都放进去,每次取最短的两条合并再放进去,直到小根堆里只剩一条纸带即可。

代码

#include<cstdio>
#include<cctype>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#define rg register
#define ll long long
using namespace std;
inline int read(){
	rg int f = 0, x = 0;
	rg char ch = getchar();
	while(!isdigit(ch))	f |= (ch == '-'), ch = getchar();
	while( isdigit(ch))	x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
	return f? -x: x;
}
int n, a[100001];
ll ans[100001];
ll maxn = 0;
int tot = 0;
priority_queue <ll, vector<ll>, greater<ll> > q;
signed main(){
	n = read();
	for(rg int i = 1; i <= n; ++i){
		a[i] = read();
		q.push(a[i]);
	}
	int u, v;
	while(!q.empty()){
		u = q.top();
		q.pop();
		if(q.empty())	break; 
		v = q.top();
		q.pop();
		q.push(u + v);
		maxn += (u + v);
		ans[++ tot] = u + v;
	}
	printf("%lld\n", maxn);
	for(rg int i = tot; i >= 1; --i){
		printf("%lld", ans[i]);
		if(i != 1)	printf(" ");
	}
	return 0;
}





7-4 序列乘积 (100 分)


代码长度限制 \(16 KB\)
时间限制 \(100 ms\)
内存限制 \(5 MB\)


Description

两个递增序列 \(A\)\(B\) ,长度都是 \(n\)。令 \(Ai\)\(Bj\) 做乘积,\(1≤i,j≤n\).请输出 \(n\times n\) 个乘积中从小到大的前 \(n\) 个。

Input

第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\).

Output

1行,\(n\) 个整数,用空格分隔,表示序列乘积中的从小到大前 \(n\) 个。

Sample Input

5
1 3 5 7 9
2 4 6 8 10

Sample Output

2 4 6 6 8

思路

求矩阵前 \(n\) 小项问题,看时间限制明显 \(O(n)\) 会TLE,所以考虑别的方法。对于矩阵 \(Sij\) 表示 \(Ai\times Bj\) ,题目上说 \(A\)\(B\) 序列是递增的,所以较小项只能出现在左侧而不是右侧,可以考虑维护一个小根堆,先把 \(A1\times Bi\)\(n\) 个值放入小根堆,此时堆内存在所有 \(B\) 序列的值,再每次取堆首,并将所取元素在 \(A\) 序列中的值向后移动一位与所对应 \(B\) 序列中的值相乘后放入小根堆,这样就能保证每次小根堆中只有 \(n\) 个元素,以达到 \(O(nlogn)\) 的复杂度,AC!

代码

#include<cstdio>
#include<cctype>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#define rg register
#define ll long long
using namespace std;
inline int read(){
	rg int f = 0, x = 0;
	rg char ch = getchar();
	while(!isdigit(ch))	f |= (ch == '-'), ch = getchar();
	while( isdigit(ch))	x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
	return f? -x: x;
}
struct node{
	int x, y;
	ll val;
	node(int x, int y, ll val) :x(x), y(y), val(val){}
	bool operator <(const node& a) const{
		return val > a.val;
	}
};
priority_queue<node> q;
int n, a[100001], b[100001], tot = 0;
signed main(){
	n = read();
	for(rg int i = 1; i <= n; ++i)	a[i] = read();
	for(rg int i = 1; i <= n; ++i)	b[i] = read();
	for(rg int i = 1; i <= n; ++i)	q.push(node(1, i, a[1] * b[i]));
	while(tot < n){
		node tmp = q.top();
		tot ++;
		printf("%lld", tmp.val);
		if(tot != n)	putchar(' ');
		q.pop();
		if(tmp.x < n)	q.push(node(tmp.x + 1, tmp.y, a[tmp.x + 1] * b[tmp.y]));
		else	q.push(node(tmp.x, tmp.y + 1, a[tmp.x] * b[tmp.y]));
	}
	return 0;
}
posted @ 2021-05-24 20:19  Horrigue_JyowYang  阅读(88)  评论(0编辑  收藏  举报