数据结构第七次上机实验-解题报告
数据结构第七次上机实验-解题报告
- 7-1 序列调度 (100 分)
- 题目
- 思路
- 参考代码
- 7-2 最大最小差 (100 分)
- 题目
- 思路
- 参考代码
- 7-3 二叉树最短路径长度 (100 分)
- 题目
- 思路
- 以先根序和中根序构建二叉树
- 树的最短路径权值和
- 参考代码
- 7-4 方案计数 (100 分)
- 题目
- 思路
- 拓扑排序
- 关键活动
- 高精度的封装
- 参考代码
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()>C
或sum==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';
}
数据结构第七次上机实验-解题报告相关推荐
- 数据结构第七次上机实验报告
7-1 序列调度 有一个N个数的序列A:1,2,--,N.有一个后进先出容器D,容器的容量为C.如果给出一个由1到N组成的序列,那么可否由A使用容器D的插入和删除操作得到. 输入格式: 第1行,2个整 ...
- JLU第三次数据结构上机实验解题报告
T1:二叉树最长路径 题目 给定一棵二叉树T,求T中的最长路径的长度,并输出此路径上各结点的值.若有多条最长路径,输出最右侧的那条. INPUT 第1行,1个整数n,表示二叉树有n个结点. 第2行,2 ...
- 数据结构荣誉课-第一次实验-解题报告
JLU-数据结构荣誉课-第一次实验-解题报告 一.重复计数 题目 思路 参考代码 二.报数游戏 题目 思路 参考代码 三.算术表达式计算 题目 思路 参考代码 四.最喜爱的序列 题目 思路 单调队列 ...
- 分数计算器java报告_20182307 2019-2020-1 《数据结构与面向对象程序设计》实验四报告...
20182307 2019-2020-1 <数据结构与面向对象程序设计>实验四报告 课程:<程序设计与数据结构> 班级: 1823 姓名: 陆彦杰 学号:20182307 实验 ...
- 20182326 2019-2020-1 《数据结构与面向对象程序设计》实验三报告
20182326 2019-2020-1 <数据结构与面向对象程序设计>实验三报告 课程:<程序设计与数据结构> 班级: 1823 姓名: 刘颖洁 学号:20182326 实验 ...
- 数据结构树的基本操作_[数据结构]树的建立与基本操作 解题报告
Problem Description 在本实验中,程序的输入是一个表示树结构的广义表.假设树的根为 root ,其子树森林 F = ( T1 , T2 , ... , Tn ),设与该树对应的广义表 ...
- 数据结构第六次上机实验
7-1 高精度数加法 (100 分) 高精度数是指大大超出了标准数据类型能表示的范围的数,例如10000位整数.很多计算问题的结果都很大,因此,高精度数极其重要. 一般使用一个数组来存储高精度数的所有 ...
- 数据库原理上机实验内容报告代码
--创建数据表 create table stu_info( stu_id char(10) not null constraint pk_stu_id primary key,--主键 name n ...
- 上海交通大学python实验七答案_20192310 实验八《数据结构与面向对象程序设计》实验报告...
20192310 2020-2021-1 <数据结构与面向对象程序设计>实验八报告 课程:<程序设计与数据结构> 班级: 1923 姓名: 严嘉钰 学号: 20192310 实 ...
最新文章
- c语言程序设计自评报告,石家庄学院c语言程序设计自评报告.docx
- 《强化学习周刊》第6期:强化学习应用之推荐系统
- mysql 1005 错误
- window和 linux 在一起 ios和 android在一起 net和js在一起
- 钉钉微应用怎么进入_钉钉微应用开发免登流程
- Learn Python the Hard Way: 字典
- C#中的多线程 - 并行编程 z
- 【Java】《Java面向对象编程的三大特性》阅读笔记
- python是一种面相什么语言_Python面相对象编程的知识总结
- iis 搭建html网站,iis6.0 、7.0添加网站(搭建网站)
- 微信小游戏 - 开发工具安装 - 官方 Demo 运行
- Netruon 理解(12):使用 Linux bridge 将 Linux network namespace 连接外网
- 【20211206】【信号处理】时频分析 —— 短时傅里叶变换(STFT)
- 从7654浏览器卸载到安装360安全卫士
- 为织梦cms列表页文章缩略图添加alt属性的方法
- java用ffmpeg获取音频信息及转化音频采样率、比特率、声道数量、格式
- 分享几个关于geoJson的网站(乡镇级地图绘制)
- 网页JS获取当前地理位置(省市区)
- 数据预处理 拉依达准则 matlab,数学建模数据预处理.doc
- poj 1013 模拟 天平问题