Noip模拟考试7:解题报告
1 . 二叉树
( ( binary .cpp/c/pas)
【问题描述】
二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
(4)没有键值相等的结点。
完全二叉树:只有最下面的两层结点度能够小于2,并且最下面一层的结点
都集中在该层最左边的若干位置的二叉树。
给出N个数,且这N个数构成1至N的排列。现在需要你按顺序构建一棵二叉
排序树,并按照层次遍历的方式输出它,然后判断它是否是一棵完全二叉树。
【输入格式】
输入文件名为binary.in。
输入文件包含两行。第一行为一个正整数N;第二行为1至N的排列。
【输出格式】
输出文件名为binary.out。
输出文件包含两行。第一行为构建出的二叉排序树的层次遍历;第二行判
断是否是完全二叉树:若是输出yes,否则输出no。
解题:二叉树实现建树。观察可知若maxo>n则输出no
maxo=n则输入yes
#include<cstdio> #include<algorithm> #include<cstring> #define maxm 1100010 #define maxn 25 using namespace std; int n,b,dep,deep,flag,Jud,maxo; int tree[maxm],a[maxn]; void build(int o,int now) { if(!tree[o]) { tree[o]=now; maxo=max(maxo,o); return ; } if(now<tree[o])build(o<<1,now); else build(o<<1|1,now); return ; } int main() { // freopen("binary.in","r",stdin); // freopen("binary.out","w",stdout); scanf("%d",&n); for(int i = 1 ; i <= n ; ++i) scanf("%d",&a[i]); tree[1]=a[1]; for(int i = 2 ; i <= n ; ++i) build(1,a[i]); for(int i = 1 ; i<=maxo; ++i) { if(!tree[i])continue; printf("%d ",tree[i]); } if(maxo>n)printf("\nno"); else printf("\nyes"); return 0; }
2 2 . 列车调度
(manage.cpp/c/pas)
【问题描述】
有N辆列车,标记为1,2,3,…,N。它们按照一定的次序进站,站台共有K个轨
道,轨道遵从 先进先出的原则。列车进入站台内的轨道后可以等待任意时间后出
站,且所有列车不可后退。现在要使出站的顺序变为N,N-1,N-2,…,1,询问K的
最小值是多少。
例如上图中进站的顺序为1,3,2,4,8,6,9,5,7,则出站的顺序变为
9,8,7,6,5,4,3,2,1。
【输入格式】
输入文件名为manage.in。
输入共2行。
第 1 行包含1个正整数N,表示N辆列车。
第 2 行包含N个正整数,为1至N的一个排列,表示进站次序。
【输出格式】
输出文件名为manage.out。
输出共1行,包含1个整数,表示站台内轨道数K的最小值。
由于每次寻找轨道中大于当前序号的最小序号,所以用二分查找实现。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int ans,top,n,a[200005],q[200005]; int main() { // freopen("manage.in","r",stdin); // freopen("manage.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); int l,r; top=1; q[1]=a[1]; for(int i=2;i<=n;i++){ if(a[i]>q[top]) { q[++top]=a[i]; continue; } l=0;r=top; while(r-l>1){ int m=(l+r)>>1; if(q[m]<a[i])l=m; else r=m; } q[r]=a[i]; } printf("%d",top); return 0; }
3. 保留道路
(road .cpp/c/pas)
【问题描述】
很久很久以前有一个国家,这个国家有N个城市,城市由1,2,3,…,N标号,
城市间有M条双向道路,每条道路都有两个属性g和s,两个城市间可能有多条道
路,并且可能存在将某一城市与其自身连接起来的道路。后来由于战争的原因,
国王不得不下令减小花费从而关闭一些道路,但是必须要保证任意两个城市相互
可达。
道路花费的计算公式为wG*max{所有剩下道路的属性g}+wS*max{所有剩下道
路的属性s},其中wG和wS是给定的值。国王想要在满足连通性的前提下使这个花
费最小,现在需要你计算出这个花费。
【输入格式】
输入文件名为road.in。
第一行包含两个正整数N和M。
第二行包含两个正整数wG和wS。
后面的M行每行描述一条道路,包含四个正整数u,v,g,s,分别表示道路连接
的两个城市以及道路的两个属性。
【输出格式】
输出文件名为road.out。
输出一个整数,表示最小花费。若无论如何不能满足连通性,输出-1。
解题:
初步考虑kruskal,由于影响因素有两个,所以我们对g 进行sort,再维护s。
每一次加边的时候,我们先将加入边存入一个维护s从小到大的数组中,然后进行kruskal,再判断边数是否=n-1,若等于n-1则更新答案。
#include<cstdio> #include<algorithm> #include<cstring> #define maxn 50010 #define maxm 410 #define inf 1ll<<60 #define ll long long using namespace std; int num,top; ll ans=inf; int fa[maxm],st[maxm],n,m; ll wg,ws; struct edge{ int u,v,g,s; }a[maxn]; int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} bool cmp(edge x,edge y){return x.g<y.g;} int main() { // freopen("road.in","r",stdin); // freopen("road.out","w",stdout); scanf("%d%d",&n,&m); scanf("%I64d%I64d",&wg,&ws); for(int i = 1 ; i <= m ; ++i) scanf("%d%d%d%d",&a[i].u,&a[i].v,&a[i].g ,&a[i].s ); sort(a+1,a+1+m,cmp); top=0; for(int i = 1 ; i <= m ; ++i) { int j; for( j = 1 ; j <= n ; ++j) fa[j]=j; for( j = top ; j >= 1 ; --j) { if(a[st[j]].s >a[i].s) st[j+1]=st[j]; else break; } top++;st[j+1]=i; num=0; for(int j = 1 ; j <= top ; ++j) { int fx=find(a[st[j]].u ); int fy=find(a[st[j]].v ); if(fx!=fy) { fa[fx]=fy; st[++num]=st[j]; } } if(num==n-1) ans=min(ans,wg*a[i].g+ws*a[st[num]].s); top=num; } if(ans==inf)printf("-1"); else printf("%I64d",ans); return 0; }