2021-05-23

第三次上机实验

指导老师: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,优化时可以考虑直接使用插入排序!!!

          维护队列过程如表格.

         

 123
11  
22  
33  

出1

 123
1x2 
22  
33  

 

出2

 123
1xx3
22  
33  

出2,结束

 123
1xx3
2x4 
33  

 

#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);
	}
	
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值