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