第三次上机实验
指导老师:Gu Fangming
7-1 二叉树最长路径 (100 分)
给定一棵二叉树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
思路:递归建树,一遍遍历。保留左节点(lft),右节点(rit),父亲节点(path),以及最大深度(max0),最好叶节点(maxi)。
#include<cstdio>
//#include <algorithm>
#include<queue>
//#include<vector>
#include<stack>
//#include<map>
#pragma warning(disable : 4996)
int lft[100010], rit[100010];
int path[100010];
int max0=0;
int maxi=1;
int make(int d) {
int zz,dep=0;
scanf("%d", &zz);
if (zz == -1) zz = 0;
if (zz) {
dep = d + 1;
lft[zz] = make(dep);
path[lft[zz]] = zz;
rit[zz] = make(dep);
path[rit[zz]]=zz;
}
if (dep >= max0) {
max0 = dep;
maxi = zz;
}
return zz;
}
using namespace std;
int main()
{ int n,m;
scanf("%d", &n);
scanf("%d", &m);
lft[m]=make(0);
rit[m] = make(0);
printf("%d\n", max0);
printf("%d",m);
int i = 1;
path[m] = 0;
while (maxi) {
lft[i] = maxi;
maxi = path[maxi];
i++;
}
i--;
while (i) {
printf(" %d", lft[i]);
i--;
}
printf("\n");
}
/*5
1 2 -1 -1 3 4 -1 -1 5 -1 -1
*/
/*7
1 2 3 -1 -1 4 5 6 -1 -1 7 -1 -1 -1 -1
correct
*//*
20
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
*/
7-2 森林的层次遍历 (100 分)
给定一个森林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
思路:保存信息,从后往前遍历,利用栈的特性建树 ,后层次遍历(队列)输出树。
#include<cstdio>
//#include <algorithm>
#include<queue>
#include<vector>
#include<stack>
//#include<map>
#pragma warning(disable : 4996)
using namespace std;
char pp[100010];
int y[100010];
vector<int> hh[100010];
stack<int> sta;
queue<int> na;
void zao(int tt) {
sta.push(tt);
for (int i = tt - 1; i >= 1; i--) {
int j = y[i];
for (; j > 0; j--) {
hh[i].push_back(sta.top());
sta.pop();
}
sta.push(i);
}
}
int main()
{
int n;
scanf("%d", &n);
getchar();
for (int i = 1; i <= n; i++) {
scanf("%c", &pp[i]);
getchar();
}
for (int i = 1; i <= n; i++) {
scanf("%d", &y[i]);
}
zao(n);
printf("%c", pp[1]);
while (!sta.empty()) {
na.push(sta.top());
sta.pop();
}
for (int i = 1; i <= y[na.front()]; i++) {
na.push(hh[na.front()][i - 1]);
}
na.pop();
while (!na.empty()) {
for (int i = 1; i <= y[na.front()]; i++) {
na.push(hh[na.front()][i-1]);
}
printf(" %c", pp[na.front()]);
na.pop();
}
}
/*5
a b c d e
2 1 0 1 0*/
7-3 纸带切割 (100 分)
有一条细长的纸带,长度为 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
思路:建好一棵huffman树,反向遍历内节点。(构造树时即保存好内节点)。
#include <iostream>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
int p[100010];
ll da[100010]; //答案
#define maxn 2*1e9
int main()
{
int t;//t is total sum
cin >> t;
for (int i = 1; i <= t; i++) {
cin >> p[i];
}
p[t + 1] = maxn;
if (t == 1) { cout << '0'; return 0; }
if (t == 2) {
ll k = p[1] + p[2];
printf("%lld\n%lld", k, k);
return 0;
}
sort(p+1, p+t+1, less<int>());
queue<int > d2;
int j = 3; //pointer to p
d2.push(p[1] + p[2]);
int k = 1; //pointer to d2
int tada = 2; //pointer to da
da[1] = p[1] + p[2];
while (j <= t || k > 1) {
ll low1, low2;
if (d2.front() <= p[j]) {
low1 = d2.front();
d2.pop();
k--;
}
else {
low1 = p[j];
j++;
}
if (k == 0) {
low2 = p[j];
j++;
}
else {
if (d2.front() <= p[j]) {
low2 = d2.front();
d2.pop();
k--;
}
else {
low2 = p[j];
j++;
}
}
d2.push(low1 + low2);
k++;
da[tada] = low1 + low2;
tada++;
}
da[t] = 0;
for (int i = 1; i <= t - 1; i++)
da[t] += da[i];
printf("%lld\n", da[t]);
printf("%lld", da[t - 1]);
for (int i = t - 2; i > 0; i--) {
printf(" %lld", da[i]);
}
}
/*
4
2 3 1 5
20
11.6.3
*/
7-4 序列乘积 (100 分)
两个递增序列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
思路:不能都算出来,那就n方了,还得排序n方longn,明显不行。
观察题目,明显按顺序的队列可以帮助优化运算,所以维持n个数据的优先队列,每次pop一个再补上一个就好了。
因为基本有序,所以排序的代价可以远远低于nlongn,优化时可以考虑直接使用插入排序!!!
维护队列过程如表格.
1 | 2 | 3 | |
1 | 1 | ||
2 | 2 | ||
3 | 3 |
出1
1 | 2 | 3 | |
1 | x | 2 | |
2 | 2 | ||
3 | 3 |
出2
1 | 2 | 3 | |
1 | x | x | 3 |
2 | 2 | ||
3 | 3 |
出2,结束
1 | 2 | 3 | |
1 | x | x | 3 |
2 | x | 4 | |
3 | 3 |
#include<cstdio>
//#include <algorithm>
#include<queue>
#include<vector>
#include<stack>
//#include<map>
#pragma warning(disable : 4996)
int a[100010], b[100010];
struct hh {
int ji;
int bb;
} j[100010];
using namespace std;
bool operator<(const hh& x,const hh& y) {
return x.ji > y.ji;
}
priority_queue<hh>tada;
int main() {
int n;
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++) {
j[i].ji = a[i] * b[1];
j[i].bb = 1;
tada.push(j[i]);
}
printf("%d", tada.top().ji);
hh ui = tada.top();
tada.pop();
ui.bb++;
ui.ji = ui.ji / b[ui.bb - 1] * b[ui.bb];
tada.push(ui);
for (int i = 2; i <= n; i++) {
printf(" %d", tada.top().ji);
hh ui = tada.top();
tada.pop();
ui.bb++;
ui.ji = ui.ji / b[ui.bb - 1] * b[ui.bb];
tada.push(ui);
}
}