数据结构第七次上机实验

7-1 序列调度 (100 分)

有一个N个数的序列A:1,2,……,N。有一个后进先出容器D,容器的容量为C。如果给出一个由1到N组成的序列,那么可否由A使用容器D的插入和删除操作得到。

输入格式:

第1行,2个整数T和C,空格分隔,分别表示询问的组数和容器的容量,1≤T≤10,1≤C≤N。

第2到T+1行,每行的第1个整数N,表示序列的元素数,1≤N≤10000。接下来N个整数,表示询问的序列。

输出格式:

T行。若第i组的序列能得到,第i行输出Yes;否则,第i行输出No,1≤i≤T。

输入样例:

在这里给出一组输入。例如:

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

输出样例:

在这里给出相应的输出。例如:

No
Yes

题目分析

即考察栈的应用,给定一个固定大小的栈,每次遇到输入数,栈顶元素小于就压入A序列,相等时弹出,若操作中产生了对于给定大小栈的溢出,则直接标记失败,接下来只循环,不操作。

代码实现如下

#include <bits/stdc++.h>
using namespace std;

#define max 1000010

int s[max];

int main(){
	int t,n,c;
	scanf("%d %d",&t,&c);
	for(int i=0;i<t;i++){
		 scanf("%d",&n);
		 int num=1,tem,flag=0,top=1;
		 s[top]=1;
		 for(int j=1;j<=n;j++){
		 	scanf("%d",&tem);
		 	while(s[top]<tem&&!flag)s[++top]=++num;
		 	if((top>c||s[top]>tem)&&!flag)flag=1;
		 	if(!flag&&s[top]==tem)top--;
		 }
		 if(flag)printf("No\n");
		 else printf("Yes\n");
	}
	return 0;
}

7-2 最大最小差 (100 分)

对n 个正整数,进行如下操作:每一次删去其中两个数 a 和 b,然后加入一个新数:a*b+1,如此下去直到 只剩下一个数。所有按这种操作方式最后得到的数中,最大的为max,最小的为min,计算max-min。

输入格式:

第1行:n,数列元素的个数,1<=n<=16。

第2行:n 个用空格隔开的数x,x<=10。

输出格式:

1行,所求max-min。

输入样例:

在这里给出一组输入。例如:

3
2 4 3

题目分析

应当证明一个定理:即每次取最小的两个数相乘加一后放回原序列,该操作(1)得到的数是最大的数max,每次取最大的两个数进行操作(2)得到的是最小的数min。

假设有三个数a<b<c,那么操作1结果为a*b*c+c,操作2结果为a*b*c+a,故可知操作1

得到的是最大数。

类似的,四个数乃至n个数也应有此规律。

代码实现如下

#include <bits/stdc++.h>
using namespace std;


priority_queue<int,vector<int>,greater<int> >q1;
priority_queue<int,vector<int>,less<int> >q2;

int main(){
	int n,t;
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		 scanf("%d",&t);
		 q1.push(t);
		 q2.push(t);
	}
	for(int i=0;i<n-1;i++){
		int x,y,a,b,tem1,tem2;
		x=q1.top();
		q1.pop();
		y=q1.top();
		q1.pop();
		tem1=x*y+1;
		q1.push(tem1);
		a=q2.top();
		q2.pop();
		b=q2.top();
		q2.pop();
		tem2=a*b+1;
		q2.push(tem2);
	}
	int max=q1.top(),min=q2.top();
	printf("%d",max-min);
	return 0;
}

值得注意的一点是,pintia平台上的样例得到的max,min均不超过int,而学校自己平台上的样例最后得到的数超过了int,应改用long long。

7-3 二叉树最短路径长度 (100 分)

给定一棵二叉树T,每个结点赋一个权值。计算从根结点到所有结点的最短路径长度。路径长度定义为:路径上的每个顶点的权值和。

输入格式:

第1行,1个整数n,表示二叉树T的结点数,结点编号1..n,1≤n≤20000。

第2行,n个整数,空格分隔,表示T的先根序列,序列中结点用编号表示。

第3行,n个整数,空格分隔,表示T的中根序列,序列中结点用编号表示。

第4行,n个整数Wi,空格分隔,表示T中结点的权值,-10000≤Wi≤10000,1≤i≤n。

输出格式:

1行,n个整数,表示根结点到其它所有结点的最短路径长度。

输入样例:

在这里给出一组输入。例如:

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

输出样例:

在这里给出相应的输出。例如:

1 0 3 3

题目分析

这题主要的考察点在于依据先根序列和中根序列确定一棵二叉树。主要思路是,在先根序列里找到根节点(不特指二叉树的根,而是递归建立二叉树时,每一个节点都可作为根),再从中根序列里确定其左右子树的大小(即有多少个元素是该根节点的左右子树里的内容),递归建立即可。

代码实现如下

#include<bits/stdc++.h>
using namespace std;

#define max 20010

int a[max],b[max],lc[max],rc[max],v[max];

queue<int> q;

int creattree(int a[],int b[],int n){
	int tem=a[0],k;
	if(n==0)return 0;
	for(k=0;k<n;k++){
		if(b[k]==tem)
			break;
	}
	lc[tem]=creattree(a+1,b,k);
	rc[tem]=creattree(a+k+1,b+k+1,n-k-1);
	return tem;
}
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]);
	}
	creattree(a+1,b+1,n);
	for(int i=1;i<=n;i++){
		scanf("%d",&v[i]);
	} 
	q.push(1);
	while(!q.empty()){
		int h=q.front();
		q.pop();
		if(lc[h]){v[lc[h]]+=v[h];q.push(lc[h]);}
		if(rc[h]){v[rc[h]]+=v[h];q.push(rc[h]);}
	}
	for(int i=1;i<n;i++){
		printf("%d ",v[i]);
	}
    printf("%d\n",v[n]);
	
	return 0;
}

7-4 方案计数 (100 分)

组装一个产品需要 n 个零件。生产每个零件都需花费一定的时间。零件的生产可以并行进行。有些零件的生产有先后关系,只有一个零件的之前的所有零件都生产完毕,才能开始生产这个零件。如何合理安排工序,才能在最少的时间内完成所有零件的生产。在保证最少时间情况下,关键方案有多少种,关键方案是指从生产开始时间到结束时间的一个零件生产序列,序列中相邻两个零件的关系属于事先给出的零件间先后关系的集合,序列中的每一个零件的生产都不能延期。

输入格式:

第1行,2个整数n和m,用空格分隔,分别表示零件数和关系数,零件编号1..n,1≤n≤10000, 0≤m≤100000 。

第2行,n个整数Ti,用空格分隔,表示零件i的生产时间,1≤i≤n,1≤Ti≤100 。

第3到m+2行,每行两个整数i和j,用空格分隔,表示零件i要在零件j之前生产。

输出格式:

第1行,1个整数,完成生产的最少时间。

第2行,1个整数,关键方案数,最多100位。

如果生产不能完成,只输出1行,包含1个整数0.

输入样例:

在这里给出一组输入。例如:

4 4
1 2 2 1
1 2
1 3
2 4
3 4

输出样例:

在这里给出相应的输出。例如:

4
2

题目分析

即考察关键路径的计算,值得注意的是,最后的到的方案数可能会有100位之多,所以应自己手写一个大整数来存储和计算方案数。

代码实现如下

#include<iostream>
#include<queue>
#include<vector>
#include<string.h>
using namespace std;

#define maxp 10010

class big_int{
public:
    int num[128],top;
    big_int(int t=0){
        top=0;
        memset(num,0,sizeof(num));
        while(t){num[top++]=t%10;t=t/10;}
    }
    big_int(big_int const& tem){
        this->top=tem.top;
        memset(this->num,0,sizeof(this->num));        
        for(int i=0;i<top;i++){
            this->num[i]=tem.num[i];
        }
    }
	big_int operator +=(big_int& a){
		*this=*this+a;
		return *this;
	}
    friend big_int operator +(big_int& a,big_int& b);
    friend ostream& operator <<(ostream& out,const big_int& tem);    

};
big_int operator +(big_int& a,big_int& b){
    big_int c;
    int top;
    if(a.top>b.top)top=a.top;
    else top=b.top;
    for(int i=0;i<top;i++){
        int tem=a.num[i]+b.num[i];
        c.num[i]+=tem;
        tem=c.num[i]/10;
        c.num[i]%=10;
        //cout<<c.num[i]<<" ";
        c.num[i+1]+=tem;
        if(i+1==top&&tem)top++;
    }
    //cout<<endl;
    c.top=top;
    return c;
}
ostream& operator <<(ostream& out,big_int& tem){    
    for(int i=tem.top-1;i>=0;i--){
        out<<tem.num[i];
    }
    if(tem.top==0)out<<"0";
    return out;
}


vector<pair<int,int> > e[maxp];
int in[maxp],out[maxp],v[maxp],ru[maxp];
int topo[maxp],top;
int ee[maxp],el[maxp];
big_int sum[maxp];
queue<int> q;

int main()
{
	//big_int x(23),y(49),c;
	//cout<<"23+49=";	
	//c=x+y;
	//cout<<c<<endl;
    int n,m;
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",&v[i]);
	}
	for(int i=0;i<m;i++){
		int x,y;
		scanf("%d %d",&x,&y);
		e[x].push_back({y,v[y]});out[x]++;in[y]++;ru[y]++;
	}
	for(int i=1;i<=n;i++){
		if(!in[i]){
			e[0].push_back({i,v[i]});
			in[i]++;ru[i]++;
			out[0]++;
		}
		if(!out[i]){
			e[i].push_back({n+1,0});
			in[n+1]++;ru[n+1]++;
			out[i]++;
		}
	}
	//topo排序求最早开始时间 
	q.push(0);
	while(!q.empty()){
		int g=q.front();
		topo[++top]=g;
		q.pop();
		for(int l=0;l<e[g].size();l++){
			pair<int,int> tem=e[g][l];
			int u=tem.first,w=tem.second;
			ru[u]--;
			if(!ru[u])q.push(u);
			if(ee[g]+w>ee[u]){
				ee[u]=ee[g]+w;
			}
		}
	}
	//逆topo序求最晚时间

	for(int i=0;i<=n+1;i++)el[i]=ee[n+1];	//初始化最晚时间数组
	

	while(top){
		int tem=topo[top--];
		for(int l=0;l<e[tem].size();l++){
			pair<int,int> vw=e[tem][l];
			int v=vw.first,w=vw.second;
			if(el[v]-w<el[tem]){
				el[tem]=el[v]-w;
			}
		}
	}
	//再次topo序求关键路径 
	q.push(0);
	sum[0]=1;
	//cout<<sum[0].top<<endl;
	while(!q.empty()){
		int g=q.front();
		q.pop();
		for(int l=0;l<e[g].size();l++){
			pair<int,int> tem=e[g][l];
			int u=tem.first,w=tem.second;
			in[u]--;
			if(!in[u])q.push(u);
			if(ee[g]==el[u]-w){
				sum[u]+=sum[g];
				//cout<<"u="<<u<<sum[u]<<endl;
			}
		}
	}
	if(sum[n+1].top!=0)cout<<ee[n+1]<<endl;
    cout<<sum[n+1]<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值