数据结构第七次上机实验-解题报告

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[10001]存储要得到的序列,使用一个栈stack<int> s;模拟1~n的入栈和出栈。
如果,栈顶元素等于要得到的序列的当前首元素,则弹栈s.pop();,且所要求序列后移一位sum++;
直到s.size()>Csum==n分别输出No或Yes。

参考代码

#include <iostream>
#include <stack> 
using namespace std;
stack<int> s;
int a[10001];

int main()
{
	int T,C;
	cin>>T>>C;
	int n;
	
	for(int i=1;i<=T;i++)
	{
		cin>>n;	
		for(int i=0;i<n;i++) cin>>a[i];
		int sum=0;//已经得到的数字的总个数
		int num=1;//计数 
		while(1)
		{
			
			while(sum!=n&&s.empty()!=1&&s.top()==a[sum])
			{
				s.pop();
				sum++;
			}
			
			if(s.size()>C)
			{
				cout<<"No"<<endl;
				break;
			}else s.push(num++);
			
			if(sum==n)
			{ 
				cout<<"Yes"<<endl;
				break;
			}
			
		}
	}
}

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

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

2

思路

这题主要是得找到一个规律,取两小相乘+1,重复执行,会得到max。同理,取两大相乘+1,重复执行,会得到min。
这里,我们可以使用优先级队列进行取两小和两大的操作。

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

其中第一个参数是容器中数据类型(int),第二个参数是容器适配器所依赖的容器类型(vector),第三个参数则是一个用于比较的一元谓词。
主体操作,就是如下代码:出队a,b和入队ab+1。

		a = q1.top();
		q1.pop();
		b = q1.top();
		q1.pop();
		q1.push(a * b + 1);

至于证明留给读者自行思考,我也不会

参考代码

#include<iostream>
#include<queue>
#include<vector>
using namespace std;
priority_queue<int, vector<int>, less<int>> q1;
priority_queue<int, vector<int>, greater<int>> q2;
int main()
{
	int n;
	cin >> n;
	int x;
	for (int i = 1; i <= n; i++)
	{
		cin >> x;
		q1.push(x);
		q2.push(x);
	}
	int a, b;
	for (int i = 1; i <= n-1; i++)
	{
		a = q1.top();
		q1.pop();
		b = q1.top();
		q1.pop();
		q1.push(a * b + 1);

		a = q2.top();
		q2.pop();
		b = q2.top();
		q2.pop();
		q2.push(a * b + 1);
	}
	cout << q2.top() - q1.top();
}

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

思路

以先根序和中根序构建二叉树

首先思考第一个问题,如何使用先序和中序构建二叉树。
举个栗子:
先根序ABCDEF
中根序BDCAFE
先根序中A为根,在中根序中找到A则A的左子树中根序为BDC,右子树中根序为FE,再回到先根序BCDEF找此时的左子树先根序为BCD,右子树先根序为EF。显然,这可以用递归来解决,参考代码如下:

void creat(Tree& T,int* preorder,int* inorder,int now_n)
{
	
	if(now_n==0)
	{
		T=NULL;
		return ;
	}//递归出口 
	
	
	int k,i;//找位置 
	T = new TreeNode;
	T->data=preorder[0];
	T->weight=value[preorder[0]];
	
//	cout<<*preorder<<" ";
	for(i = 0; i<now_n; i++)//找位置 
	if(inorder[i] == preorder[0])
	break;
	
	k=i; 
	creat(T->left,preorder+1,inorder,k);
	//递归调用本算法创建左子树
	creat(T->right,preorder+k+1,inorder+k+1,now_n-k-1);
	//递归调用本算法创建右子树

}

树的最短路径权值和

跟打印每条路径的思路差不多,只不过不是打印,而是在遍历时存储并相加每个点的权值得到sum。

void printpath(Tree& T,int sum) {
	//二叉树中从每个叶子结点到根结点的路径
	
	//cout<<s<<" ";
	if (T != NULL) {
		int s=sum+T->weight;//将当前结点权值放入路径中
		ret[T->data]=s;
		if (T->left == NULL && T->right == NULL) {//叶子结点
		    
		} else {
			printpath(T->left,s);
			printpath(T->right,s);
		}
	}
}

参考代码

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

typedef struct node {
	int data;
	struct node* left;
	struct node* right;
	int weight;
} TreeNode, * Tree;

int n;
int preorder[20001];
int inorder[20001];
int value[20001];
int ret[20001];
void creat(Tree& T,int* preorder,int* inorder,int now_n)
{
	
	if(now_n==0)
	{
		T=NULL;
		return ;
	}//递归出口 
	
	
	int k,i;//找位置 
	T = new TreeNode;
	T->data=preorder[0];
	T->weight=value[preorder[0]];
	
//	cout<<*preorder<<" ";
	for(i = 0; i<now_n; i++)//找位置 
	if(inorder[i] == preorder[0])
	break;
	
	k=i; 
	creat(T->left,preorder+1,inorder,k);
	//递归调用本算法创建左子树
	creat(T->right,preorder+k+1,inorder+k+1,now_n-k-1);
	//递归调用本算法创建右子树

}


void printpath(Tree& T,int sum) {
	//二叉树中从每个叶子结点到根结点的路径
	
	//cout<<s<<" ";
	if (T != NULL) {
		int s=sum+T->weight;//将当前结点权值放入路径中
		ret[T->data]=s;
		if (T->left == NULL && T->right == NULL) {//叶子结点
		    
		} else {
			printpath(T->left,s);
			printpath(T->right,s);
		}
	}
}


int main() {
	cin>>n;
	for(int i=0; i<n; i++) cin>>preorder[i];
	for(int i=0; i<n; i++) cin>>inorder[i];
	for(int i=1; i<=n; i++) cin>>value[i];
	
	Tree T;
	creat(T,preorder,inorder,n);
	int sum=0;
	printpath(T,sum);
	//ret[T->data]=T->weight;
	for(int i=1;i<n;i++) cout<<ret[i]<<" ";
	cout<<ret[n]<<endl;
}

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位数,故还得使用高精度的算法。

拓扑排序

首先,我们得先运用虚源虚汇的方法将点权转化为边权。(就是设个虚源,边权为0,并把每个点的点权后移到对应边上即可)
至于拓扑排序如何进行,在以前的文章中已经讨论过了,在此便不再赘述。(在这次的方法中,使用vector+pair的形式存储,first为该点的邻接点,second为到达该邻接点的边权)

stack<int> s;
int tpxv[10100];
int rudu[10100];
vector<pair<int,int> > v[10100];//first为邻接点,second为权值 
void Tpsort(int n)
{

	s.push(0);//虚源
	int i=0,j=0;
	for(i=0;i<=n+1;i++)
	{
		if(s.empty()==1)
		{
			cout<<'0'<<'\n';
			exit(0);
		}
		
		int x=s.top();
		s.pop();
		tpxv[j]=x;
		++j;
		vector<pair<int,int> >::iterator it=v[x].begin();
		for(;it!=v[x].end();++it)
		{
			rudu[it->first]--;
			if(rudu[it->first]==0)
			s.push(it->first);
		}
		
	}
}

关键活动

使用正序拓扑序列求一遍各点最早开始时间ve,再使用逆序拓扑序列求一遍最晚开始时间vl。ve=vl的点,即是关键活动点。

int ve[10100];
int vl[10100];
void CriticalPath(int n)
{
	int i=0;
	Tpsort(n);
	for(i=0;i<=n;++i)
	{
		vector<pair<int,int> >::iterator it=v[tpxv[i]].begin();
		for(;it!=v[tpxv[i]].end();++it)
		{
			if(ve[it->first]<ve[tpxv[i]]+it->second)
			ve[it->first]=ve[tpxv[i]]+it->second; 
		}
	}
	
	
	for(i=0;i<=n+1;i++) vl[i]=ve[n+1];//逆序求最迟
	
	for(i=n;i>=0;--i)
	{
		vector<pair<int,int> >::iterator it=v[tpxv[i]].begin();
		for(;it!=v[tpxv[i]].end();++it)
		{
			if(vl[it->first]<vl[tpxv[i]]+it->second)
			vl[tpxv[i]]=vl[it->first]-it->second; 
		}
	}
	
}

高精度的封装

将输出的大数,一位位存储进vector,来个reverse反转一下,就是一位位的大数,具体实现在前几次的题解中的大数加法完全类似,这里只是做了一下封装,故不再赘述。


class GJD//高精度 
{
	public:
		vector<int> v;//存储高精度数 
		int flag;//标记 
		
		GJD(int=0);
		GJD operator=(GJD gjd);
		GJD operator+(GJD gjd);
		GJD &operator+=(GJD gjd);
		GJD operator-(GJD gjd);
		friend ostream &operator<<(ostream &out,GJD &gjd);
		//~GJD()=0;
};

ostream &operator<<(ostream &out,GJD &gjd)
{
	for(int i=0;i<gjd.v.size();++i) out<<gjd.v.at(i);
	return out;
}

GJD::GJD(int n)
{
	int temp=n;
	if(temp==0)
	{
		flag=0;
		v.push_back(0);
	}
	else
	{
		flag=1;
		while(temp!=0)
		{
			v.push_back(temp%10);
			temp=temp/10;
		}
		reverse(v.begin(),v.end());
	}
}
GJD GJD::operator=(GJD gjd)
{
	v.assign(gjd.v.begin(),gjd.v.end());
	flag=gjd.flag;
	
	return *this;
}

GJD GJD::operator+(GJD gjd)
{
	GJD ret;
	ret.v.clear();
	ret.flag=gjd.flag;
	int i=this->v.size()-1;
	int j=gjd.v.size()-1;
	
	int fg=0;//标记进位 
	while(i>=0&&j>=0)
	{
		int temp=this->v.at(i)+gjd.v.at(j);
		if(fg==1){fg=0;temp=temp+1;};
		if(temp>=10){fg=1;temp=temp-10;};
		ret.v.push_back(temp);
		--i;--j;
	}
	while(i>=0)
	{
		int temp=this->v.at(i);
		if(fg==1){fg=0;temp=temp+1;};
		if(temp>=10){fg=1;temp=temp-10;};
		ret.v.push_back(temp);
		--i;
	}
	while(j>=0)
	{
		int temp=gjd.v.at(j);
		if(fg==1){fg=0;temp=temp+1;};
		if(temp>=10){fg=1;temp=temp-10;};
		ret.v.push_back(temp);
		--j;
	}
	if(fg==1)
	{
		ret.v.push_back(1);
		fg=0;
	}
	reverse(ret.v.begin(),ret.v.end());
	return ret;
}

GJD GJD::operator-(GJD gjd)
{
	GJD ret;
	ret.v.clear();
	ret.flag=gjd.flag;
	int i=this->v.size()-1;
	int j=gjd.v.size()-1;
	
	int fg=0;//标记进位 
	while(i>=0&&j>=0)
	{
		int temp=this->v.at(i)-gjd.v.at(j);
		if(fg==1){fg=0;temp=temp-1;};
		if(temp<0){fg=1;temp=temp+10;};
		ret.v.push_back(temp);
		--i;--j;
	}
	while(i>=0)
	{
		int temp=this->v.at(i);
		if(fg==1){fg=0;temp=temp-1;};
		if(temp<0){fg=1;temp=temp+10;};
		ret.v.push_back(temp);
		--i;
	}
//	while(j>=0)
//	{
//		int temp=gjd.v.at(j);
//		if(fg==1){fg=0;temp=temp+1;};
//		if(temp>=10){fg=1;temp=temp-10;};
//		ret.v.push_back(temp);
//		--j;
//	}


//	if(fg==1)
//	{
//		ret.v.push_back(1);
//		fg=0;
//	}

    int f=ret.v.size()-1;
    while(!ret.v.at(f))
    {
    	ret.v.pop_back();
    	--f;
	}

	reverse(ret.v.begin(),ret.v.end());
	return ret;
}

GJD &GJD::operator+=(GJD gjd)
{
	*this=*this+gjd;
	return *this;
}

参考代码

有了上述的关键路径已经高精度算法,只需使用dfs遍历各个关键活动点(ve=vl的点),并在DFS(x)的开始,记录jieguo的值。

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

class GJD//高精度 
{
	public:
		vector<int> v;//存储高精度数 
		int flag;//标记 
		
		GJD(int=0);
		GJD operator=(GJD gjd);
		GJD operator+(GJD gjd);
		GJD &operator+=(GJD gjd);
		GJD operator-(GJD gjd);
		friend ostream &operator<<(ostream &out,GJD &gjd);
		//~GJD()=0;
};

ostream &operator<<(ostream &out,GJD &gjd)
{
	for(int i=0;i<gjd.v.size();++i) out<<gjd.v.at(i);
	return out;
}

GJD::GJD(int n)
{
	int temp=n;
	if(temp==0)
	{
		flag=0;
		v.push_back(0);
	}
	else
	{
		flag=1;
		while(temp!=0)
		{
			v.push_back(temp%10);
			temp=temp/10;
		}
		reverse(v.begin(),v.end());
	}
}
GJD GJD::operator=(GJD gjd)
{
	v.assign(gjd.v.begin(),gjd.v.end());
	flag=gjd.flag;
	
	return *this;
}

GJD GJD::operator+(GJD gjd)
{
	GJD ret;
	ret.v.clear();
	ret.flag=gjd.flag;
	int i=this->v.size()-1;
	int j=gjd.v.size()-1;
	
	int fg=0;//标记进位 
	while(i>=0&&j>=0)
	{
		int temp=this->v.at(i)+gjd.v.at(j);
		if(fg==1){fg=0;temp=temp+1;};
		if(temp>=10){fg=1;temp=temp-10;};
		ret.v.push_back(temp);
		--i;--j;
	}
	while(i>=0)
	{
		int temp=this->v.at(i);
		if(fg==1){fg=0;temp=temp+1;};
		if(temp>=10){fg=1;temp=temp-10;};
		ret.v.push_back(temp);
		--i;
	}
	while(j>=0)
	{
		int temp=gjd.v.at(j);
		if(fg==1){fg=0;temp=temp+1;};
		if(temp>=10){fg=1;temp=temp-10;};
		ret.v.push_back(temp);
		--j;
	}
	if(fg==1)
	{
		ret.v.push_back(1);
		fg=0;
	}
	reverse(ret.v.begin(),ret.v.end());
	return ret;
}

GJD GJD::operator-(GJD gjd)
{
	GJD ret;
	ret.v.clear();
	ret.flag=gjd.flag;
	int i=this->v.size()-1;
	int j=gjd.v.size()-1;
	
	int fg=0;//标记进位 
	while(i>=0&&j>=0)
	{
		int temp=this->v.at(i)-gjd.v.at(j);
		if(fg==1){fg=0;temp=temp-1;};
		if(temp<0){fg=1;temp=temp+10;};
		ret.v.push_back(temp);
		--i;--j;
	}
	while(i>=0)
	{
		int temp=this->v.at(i);
		if(fg==1){fg=0;temp=temp-1;};
		if(temp<0){fg=1;temp=temp+10;};
		ret.v.push_back(temp);
		--i;
	}
//	while(j>=0)
//	{
//		int temp=gjd.v.at(j);
//		if(fg==1){fg=0;temp=temp+1;};
//		if(temp>=10){fg=1;temp=temp-10;};
//		ret.v.push_back(temp);
//		--j;
//	}


//	if(fg==1)
//	{
//		ret.v.push_back(1);
//		fg=0;
//	}

    int f=ret.v.size()-1;
    while(!ret.v.at(f))
    {
    	ret.v.pop_back();
    	--f;
	}

	reverse(ret.v.begin(),ret.v.end());
	return ret;
}

GJD &GJD::operator+=(GJD gjd)
{
	*this=*this+gjd;
	return *this;
}

stack<int> s;
int tpxv[10100];
int rudu[10100];
vector<pair<int,int> > v[10100];//first为邻接点,second为权值 


void Tpsort(int n)
{

	s.push(0);//虚源
	int i=0,j=0;
	for(i=0;i<=n+1;i++)
	{
		if(s.empty()==1)
		{
			cout<<'0'<<'\n';
			exit(0);
		}
		
		int x=s.top();
		s.pop();
		tpxv[j]=x;
		++j;
		vector<pair<int,int> >::iterator it=v[x].begin();
		for(;it!=v[x].end();++it)
		{
			rudu[it->first]--;
			if(rudu[it->first]==0)
			s.push(it->first);
		}
		
	}
}

int ve[10100];
int vl[10100];
void CriticalPath(int n)
{
	int i=0;
	Tpsort(n);
	for(i=0;i<=n;++i)
	{
		vector<pair<int,int> >::iterator it=v[tpxv[i]].begin();
		for(;it!=v[tpxv[i]].end();++it)
		{
			if(ve[it->first]<ve[tpxv[i]]+it->second)
			ve[it->first]=ve[tpxv[i]]+it->second; 
		}
	}
	
	
	for(i=0;i<=n+1;i++) vl[i]=ve[n+1];//逆序求最迟
	
	for(i=n;i>=0;--i)
	{
		vector<pair<int,int> >::iterator it=v[tpxv[i]].begin();
		for(;it!=v[tpxv[i]].end();++it)
		{
			if(vl[it->first]<vl[tpxv[i]]+it->second)
			vl[tpxv[i]]=vl[it->first]-it->second; 
		}
	}
	
}

GJD jieguo;
GJD vis[10100];

void DFS(int x,int n)
{
	if(x==n+1)
	{
		jieguo=jieguo+1;
		return ;
	}
	GJD tmp=jieguo;
	vector<pair<int,int> >::iterator it=v[x].begin();
	for(;it!=v[x].end();++it)//遍历其所有邻边
	{
		if(vis[it->first].flag==1)//如果已经求出其后面的关键路径数
		{
			jieguo=jieguo+vis[it->first]-1;
			continue;
		}
		if(ve[it->first]==vl[it->first])//只有ve=vl才是关键路径上的点
		DFS(it->first,n);
	}

	vis[x]=jieguo-tmp+1;

}

int weight[10100];

int main()
{
//	ios::sync_with_stdio(0);
//	cin.tie(0);
	int N,M;
	cin>>N>>M;
	for(int i=1;i<=N;i++) cin>>weight[i];
	int u,V;
	for(int i=1;i<=M;i++)//初始化 
	{
		cin>>u>>V;
		v[u].push_back(make_pair(V,weight[u]));
		rudu[V]++;
	}
	
	for(int i=1;i<=N;i++)
	{
		if(rudu[i]==0)
		{
			v[0].push_back(make_pair(i,0));
			rudu[i]++;
		}
	}
	
	for(int i=1;i<=N;i++)
	{
		if(v[i].size()==0)
		{
			v[i].push_back(make_pair(N+1,weight[i]));
			rudu[N+1]++;
		}
	}
	
	CriticalPath(N);
	DFS(0,N);
	cout<<vl[N+1]<<'\n';
	cout<<jieguo<<'\n'; 
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值