i信息学奥赛

加入QQ群:1025629106,或关注微信公众号:i信息学奥赛,获取更多学习资源。

导航

完善程序

Posted on 2019-10-04 14:17  shnoip  阅读(962)  评论(0编辑  收藏  举报

091、(最大连续子段和)给出一个数列(元素个数不多于100),数列元素均为负整数、正整数、0。请找出数列中的一个连续子数列,使得这个子数列中包含的所有元素之和最大,在和最大的前提下还要求该子数列包含的元素个数最多,并输出这个最大和以及连续子数列中元素的个数。例如数列为 4,-5,3,2,4 时,输出 9 和 3;数列为 1 2 3 -5 0 7 8 时,输出 16 和 7。
#include<iostream>
using namespace std;
int a[101],n,i,ans,len,tmp,beg;
int main() {
  cin>>n;
  for (i=1;i<=n;i++) cin>>a[i];
  tmp=ans=len=0;
  beg=__1__;
  for (i=1;i<=n;i++) {
    if (tmp+a[i]>ans) ans=tmp+a[i],len=i-beg;
    else if (__2__ && i-beg>len) len=i-beg;
    if (tmp+a[i]__3__) beg=__4__,tmp=0;
    else __5__;
  }
  cout<<ans<<' '<<len;
  return 0;
}

122、(排列数)输入两个正整数 n,m(1<=n<=20,1<=m<=n),在 1-n 中任取 m 个数,按字典序从小到大输出所有这样的排列。
例如,
输入:3 2
输出:
1 2
1 3
2 1
2 3
3 1
3 2

#include<iostream>
#include<cstring>
using namespace std;
const int SIZE=25;
bool used[SIZE];
int data[SIZE];
int n,m,i,j,k;
bool flag;
int main() {
  cin>>n>>m;
  memset(used,false,sizeof(used));
  for (i=1;i<=m;i++) data[i]=i,used[i]=true;
  flag=true;
  while (flag) {
    for (i=1;i<=m-1;i++) cout<<data[i]<<' ';
    cout<<data[m]<<endl;
    flag=__1__;
    for (i=m;i>=1;i--) {
      __2__;
      for (j=data[i]+1;j<=n;j++)
        if (!used[j]) {
          used[j]=true;
          data[i]=__3__;
          flag=true;
          break;
        }
      if (flag) {
        for (k=i+1;k<=m;k++)
          for (j=1;j<=__4__;j++)
            if (!used[j]) data[k]=j,used[j]=true,break;
        __5__;
      }
    }
  }
}

131、(序列重排)全局数组变量 a 定义如下:
const int SIZE=100;
int a[SIZE],n;
它记录着一个长度为 n 的序列 a[1],a[2],…,a[n]。
现在需要一个函数,以整数 p(1<=p<=n) 为参数,实现如下功能:将序列 a 的前 p 个数与后 n-p 个数对调,且不改变这 p 个数(或 n-p 个数)之间的相对位置。例如,长度为 5 的序列 1,2,3,4,5,当 p=2 时重排结果为 3,4,5,1,2。
有一种朴素的算法可以实现这一需求,其时间复杂度为O(n)、空间复杂度为O(n):
void swap1(int p) {
  int i,j,b[SIZE];
  for (i=1;i<=p;i++) b[__1__]=a[i];
  for (i=p+1;i<=n;i++) b[i-p]=__2__;
  for (i=1;i<=__3__;i++) a[i]=b[i];
}
我们也可以用时间换空间,使用时间复杂度为O(n*n)、空间复杂度为O(1)的算法:
void swap2(int p) {
  int i,j,temp;
  for (i=p+1;i<=n;i++) {
    temp=a[i];
    for (j=i;j>=__4__;j--) a[j]=a[j-1];
    __5__=temp;
  }
}

132、(二叉查找树)二叉查找树具有如下性质:每个节点的值都大于其左子树上所有节点的值、小于其右子树上所有节点的值。试判断一棵树是否为二叉查找树。
输入的第一行包含一个整数 n,表示这棵树有 n 个顶点,编号分别为 1,2,…,n,其中编号为 1 的为根结点。之后的第 i 行有三个数 value,left_child,right_child,分别表示该节点关键字的值、左子节点的编号、右子节点的编号;如果不存在左子节点或右子节点,则用 0 代替。输出 1 表示这棵树是二叉查找树,输出 0 则表示不是。
#include<iostream>
using namespace std;
const int SIZE=100;
const int INFINITE=1000000;
struct node {
  int left_child,right_child,value;
} a[SIZE];
int is_bst (int root,int lower_bound,int upper_bound) {
  if (root==0) return 1;
  int cur=a[root].value;
  if ((cur>lower_bound) && (__1__) &&
     (is_bst(a[root].left_child,lower_bound,cur)==1) &&
     (is_bst(__2__,__3__,__4__)==1)) return 1;
  return 0;
}
int main() {
  int i,n;
  cin>>n;
  for (i=1;i<=n;i++) cin>>a[i].value>>a[i].left_child>>a[i].right_child;
  cout<<is_bst(__5__,-INFINITE,INFINITE);
  return 0;
}

142、(最大子矩阵和)给出 m 行 n 列的整数矩阵,求最大的子矩阵和(子矩阵不能为空)。
输入第一行包含两个整数 m 和 n,即矩阵的行数和列数。之后 m 行,每行 n 个整数,描述整个矩阵。程序最终输出最大的子矩阵和。(最后一空 4 分,其余 3 分,共 16 分。)
#include<iostream>
using namespace std;
const int SIZE=100;
int matrix[SIZE+1][SIZE+1];
int rowsum[SIZE+1][SIZE+1];  //rowsum[i][j]记录第 i 行前 j 个数的和
int m,n,i,j,first,last,area,ans;
int main() {
  cin>>m>>n;
  for (i=1;i<=m;i++)
    for (j=1;j<=n;j++) cin>>matrix[i][j];
  ans=matrix__1__;
  for (i=1;i<=m;i++) __2__;
  for (i=1;i<=m;i++)
    for (j=1;j<=n;j++) rowsum[i][j]=__3__;
  for (first=1;first<=n;first++)
    for (last=first;last<=n;last++) {
      __4__;
      for (i=1;i<=m;i++) {
        area+=__5__;
        if (area>ans) ans=area;
        if (area<0) area=0;
      }
    }
  cout<<ans;
  return 0;
}

T131、(序列重排)全局数组变量 a 定义如下:
const int SIZE=100;
int a[SIZE],n;
它记录着一个长度为 n 的序列 a[1],a[2],…,a[n]。
现在需要一个函数,以整数 p(1<=p<=n) 为参数,实现如下功能:将序列 a 的前 p 个数与后 n-p 个数对调,且不改变这 p 个数(或 n-p 个数)之间的相对位置。例如,长度为 5 的序列 1,2,3,4,5,当 p=2 时重排结果为 3,4,5,1,2。
有一种朴素的算法可以实现这一需求,其时间复杂度为O(n)、空间复杂度为O(n):
void swap1(int p) {
  int i,j,b[SIZE];
  for (i=1;i<=p;i++) b[__1__]=a[i];
  for (i=p+1;i<=n;i++) b[i-p]=a[i];
  for (i=1;i<=n;i++) a[i]=b[i];
}
我们也可以用时间换空间,使用时间复杂度为O(n*n)、空间复杂度为O(1)的算法:
void swap2(int p) {
  int i,j,temp;
  for (i=p+1;i<=n;i++) {
    temp=a[i];
    for (j=i;j>=__2__;j--) a[j]=a[j-1];
    __3__=temp;
  }
}
事实上,还有一种更好的算法,时间复杂度为O(n)、空间复杂度为O(1);
void swap3(int p) {
  int start1,end1,start2,end2,i,j,temp;
  start1=1;
  end1=p;
  start2=p+1;
  end2=n;
  while (true) {
    i=start1;
    j=start2;
    while ((i<=end1) && (j<=end2)) swap(a[i],a[j]),i++,j++;
    if (i<=end1) start1=i;
    else if (__4__) start1=__5__,end1=__6__,start2=j;
         else break;
  }
}

T132、(两元序列)试求一个整数序列中,最长的仅包含两个不同整数的连续子序列。如果有多个子序列并列最长,最出任意一个即可。例如,序列“1 1 2 3 2 3 2 3 3 1 1 1 3 1”中,有两段满足条件的最长子序列,长度均为 7,分别是“2 3 2 3 2 3 3”和“3 3 1 1 1 3 1”。
#include<iostream>
using namespace std;
int main() {
  const int SIZE=100;
  int n,i,j,a[SIZE],cur1,cur2,count1,count2,ans_length,ans_start,ans_end;
  //cur1,cur2分别表示当前子序列中的两个不同整数
  //count1,count2分别表示 cur1,cur2 在当前子序列中出现的次数
  cin>>n;
  for (i=1;i<=n;i++) cin>>a[i];
  i=j=1;  //i,j分别表示当前子序列的首尾,并保证其中至少有两个不同整数
  while((i<=n) && (a[i]==a[j])) j++;
  cur1=a[i];cur2=a[j];
  count1=__1__;count2=1;
  ans_length=j-i+1;
  while (j<n) {
    j++;
    if (a[j]==cur1) count1++;
    else if (a[j]==cur2) count2++;
         else {
           if (a[j-1]==__2__) {
             while (count2>0) {
               if (a[i]==cur1) count1--;
               else count2--;
               i++;
             }
             cur2=a[j];count2=1;
           } else {
               while (count1>0) {
                 if (a[i]==cur1) __3__;
                 else __4__;
               }
               __5__,count1=1;
             }
         }
    if (ans_length<j-i+1) ans_length=j-i+1,ans_start=i,ans_end=j;
  }
  for (i=ans_start;i<=ans_end;i++) cout<<a[i]<<' ';
  return 0;
}


T141、(双栈模拟数组)只使用两个栈结构 stack1 和 stack2,模拟对数组的随机读取。作为栈结构,stack1 和 stack2 只能访问栈顶(最后一个有效元素)。栈顶指针 top1 和 top2 均指向栈顶元素的下一个位置。
输入第一行包含两个整数,分别是数组长度 n 和访问次数 m,中间用单个空格隔开。第二行包含 n 个整数,依次给出数组各项(数组下标从 0 到 n-1)。第三行包含 m 个整数,需要访问的数组下标。对于每次访问,输出对应的数组元素。
#include<iostream>
using namespace std;
const int SIZE=100;
int stack1[SIZE],stack2[SIZE];
int top1,top2,n,m,i,j;
void clearStack() {
  int i;
  for (i=top1;i<SIZE;i++) stack1[i]=0;
  for (i=top2;i<SIZE;i++) stack2[i]=0;  
}
int main() {
  cin>>n>>m;
  for (i=0;i<n;i++) cin>>stack1[i];
  top1=__1__;top2=__2__;
  for (j=0;j<m;j++) {
    cin>>i;
    while (i<top1-1) top1--,__3__,top2++;
    while (i>top1-1) top2--,__4__,top1++;
    clearStack();
    cout<<stack1[__5__]<<endl;
  }
  return 0;
}

T151、(双子序列最大和)给定一个长度为 n(3<=n<=1000)的整数序列,要求从中选出两个连续子序列,使得这两个连续子序列的序列和之和最大,最终只需输出这个最大和。一个连续子序列的序列和为该连续子序列中所有数之和。要求:每个连续子序列长度至少为 1,且两个连续子序列之间至少间隔 1 个数。(第五空 4 分,其余 2.5 分。)
#include<iostream>
using namespace std;
const int MAXN=1000;
int n,i,ans,sum,x[MAXN];
int lmax[MAXN];
//lmax[i]为仅含 x[i] 及 x[i] 左侧整数的连续子序列的序列和中,最大的序列和
int rmax[MAXN];
//rmax[i]为仅含 x[i] 及 x[i] 右侧整数的连续子序列的序列和中,最大的序列和
int main() {
  cin>>n;
  for (i=0;i<n;i++) cin>>x[i];
  lmax[0]=x[0];
  for (i=1;i<n;i++)
    if (lmax[i-1]<=0) lmax[i]=x[i];
    else lmax[i]=lmax[i-1]+x[i];
  for (i=1;i<n;i++)
    if (lmax[i]<lmax[i-1]) lmax[i]=lmax[i-1];
  __1__;
  for (i=n-2;i>=0;i--)
    if (rmax[i+1]<=0) __2__;
    else __3__;
  for (i=n-2;i>=0;i--)
    if (rmax[i]<rmax[i+1]) __4__;
  ans=x[0]+x[2];
  for (i=1;i<n-1;i++) {
    sum=__5__;
    if (sum>ans) ans=sum;
  }
  cout<<ans<<endl;
  return 0;
}

T152、(最短路径问题)无向连通图 G 有 n 个结点,依次编号为 1,2,3,……,n。用邻接矩阵的形式给出每条边的边长,要求输出以结点 1 为起点出发,到各结点的最短路径长度。
使用 Dijkstra 算法解决该问题:利用 dist 数组记录当前各结点与起点的已找到的最短路径长度;每次从未扩展的结点中选取 dist 值最小的结点 v 进行扩展,更新与 v 相邻的结点的 dist 值;不断进行上述操作直至所有结点均被扩展,此时 dist 数据中记录的值即为各结点与起点的最短路径长度。(第五空 2 分,其余 3 分。)
#include<iostream>
using namespace std;
const int MAXV=100;
int n,i,j,v,
int w[MAXV][MAXV];
//邻接矩阵,记录边长
//其中 w[i][j] 为连接结点 i 和结点 j 的无向边长度,若无边则为 -1
int dist[MAXV];
int used[MAXV];
//记录结点是否已扩展(0:未扩展;1:已扩展)
int main() {
  cin>>n;
  for (i=0;i<n;i++)
    for (j=0;j<n;j++)
      cin>>w[i][j];
  dist[0]=0;
  for (i=1;i<n;i++) dist[i]=-1;
  for (i=0;i<n;i++) used[i]=0;
  while (true) {
    __1__;
    for (i=0;i<n;i++)
      if (used[i]!=1 && dist[i]!=-1 && (v==-1 || __2__))
        __3__;
    if (v==-1) break;
    __4__;
    for (i=0;i<n;i++)
      if (w[v][i]!=-1 && (dist[i]==-1 || __5__))
        dist[i]=dist[v]+w[v][i];
  }
  for (i=0;i<n;i++) cout<<dist[i]<<endl;
  return 0;
}

T161、(交朋友)根据社会学研究表明,人们都喜欢找和自己身高相近的人做朋友。现在有 n 名身高两两不相同的同学依次走入教室,调查人员想预测每个人在走入教室的瞬间想和已经进入教室的哪个人做朋友。当有两名同学和这名同学的身高差一样时,这名同学会更想和高的那个人做朋友。比如一名身高为 1.80 米的同学进入教室时,有一名身高为  1.79 米的同学和一名身高为  1.81 米的同学在教室里,那么这名身高为 1.80 米的同学会更想和身高为 1.81 米的同学做朋友。对于第一个走入教室的同学我们不做预测。
由于我们知道所有人的身高和走进教室的次序,所以我们可以采用离线的做法来解决这样的问题,我们用排序加链表的方式帮助每一个人找到在他之前进入教室的并且和他身高最相近的人。(第一空 2 分,其余 3 分)
#include<iostream>
using namespace std;
#define MAXN 200000
#define infinity 2147483647
int answer[MAXN],height[MAXN],previous[MAXN],next[MAXN],rank[MAXN],n;
void sort(int l,int r) {
  int x=height[rank[(l+r)/2]],i=l,j=r,temp;
  while (i<=j) {
    while (height[rank[i]]<x) i++;
    while (height[rank[j]]>x) j--;
    if (__1__) swap(rank[i],rank[j]),i++,j--;
  }
  if (i<r) sort(i,r);
  if (l<j) sort(l,j);
}
int main() {
  cin>>n;
  int i,higher,shorter;
  for (i=1;i<=n;i++) cin>>height[i],rank[i]=i;
  sort(1,n);
  for (i=1;i<=n;i++) {
    previous[rank[i]]=rank[i-1];
    __2__;
  }
  for (i=n;i>=2;i--) {
    higher=shorter=infinity;
    if (previous[i]!=0) shorter=height[i]-height[previous[i]];
    if (next[i]!=0) __3__;
    if (__4__) answer[i]=previous[i];
    else answer[i]=next[i];
    next[previous[i]]=next[i];
    __5__;
  }
  for (i=2;i<=n;i++) cout<<i<<":"<<answer[i];
  return 0;
}

T162、(交通中断)有一个小国家,国家内有 n 座城市和 m 条双向的道路,每条道路连接着两座不同的城市。其中 1 号城市为国家的首都。由于地震频繁可能导致某一个城市与外界交通全部中断。这个国家的首脑想知道,如果只有第 i(i>1)个城市因地震而导致交通中断时,首都到多少个城市的最短路径长度会发生改变。如果因为无法通过第 i 个城市而导致从首都出发无法到达某个城市,也认为到达该城市的最短路径长度改变。
对于每一个城市 i,假定只有第 i 个城市与外界交通中断,输出有多少个城市会因此导致到首都的最短路径长度改变。
我们采用邻接表的方式存储图的信息,其中 head[x] 表示顶点 x 的第一条边的编号,next[i] 表示第 i 条边的下一条边的编号,point[i] 表示第 i 条边的终点,weight[i] 表示第 i 条边的长度。(第一空 2 分,其余 3 分。)
#include<iostream>
#include<cstring>
using namespace std;
#define MAXN 6000
#define MAXM 100000
#define infinity 2147483647
int head[MAXN],next[MAXM],point[MAXM],weight[MAXM];
int queue[MAXN],dist[MAXN],visit[MAXN];
int n,m,x,y,z,total=0,answer;
void link(int x,int y,int z) {
  total++;
  next[total]=head[x];
  head[x]=total;
  point[total]=y;
  weight[total]=z;
  total++;
  next[total]=head[y];
  head[y]=total;
  point[total]=x;
  weight[total]=z;
}
int main() {
  int i,j,s,t;
  cin>>n>>m;
  for (i=1;i<=m;i++) cin>>x>>y>>z,link(x,y,z);
  for (i=1;i<=n;i++) dist[i]=infinity;
  __1__;
  queue[1]=1;visit[1]=1;
  s=t=1;
  //使用 SPFA 求出第一个点到其余各点的最短路长度
  while (s<=t) {
    x=queue[s%MAXN];
    j=head[x];
    while (j!=0) {
      if (__2__) {
        dist[point[j]]=dist[x]+weight[j];
        if (visit[point[j]]==0) t++,queue[t%MAXN]=point[j],visit[point[j]]=1;
      }
      j=next[j];
    }
    __3__;s++;
  }
  for (i=2;i<=n;i++) {
    queue[1]=1;memset(visit,0,sizeof(visit));visit[1]=1;
    s=t=1;
    while (s<=t) {  //判断最短路长度是否不变
      x=queue[s];
      j=head[x];
      while (j!=0) {
        if (point[j]!=i && __4__ && visit[point[j]]==0) __5__,t++,queue[t]=point[j];
        j=next[j];
      }
      s++;
    }
    answer=0;
    for (j=1;j<=n;j++) answer+=1-visit[j];
    cout<<i<<':'<<answer-1;
  }
  return 0;
}

答案:
091
1)、0
2)、tmp+a[i]==ans
3)、<0
4)、i
5)、tmp+=a[i]

122
1)、0
2)、used[data[i]]=false
3)、j
4)、n
5)、break

131
1)、n-p+i
2)、a[i]
3)、n
4)、i-p+1
5)、a[i-p]

132
1)、cur<upper_bound
2)、a[root].right_child
3)、cur
4)、upper_bound
5)、1

142
1)、[1][1]
2)、rowsum[i][0]=0
3)、rowsum[i][j-1]+matrix[i][j]
4)、area=0
5)、rowsum[i][last]-rowsum[i][first-1]

T131
1)、n-p+i
2)、i-p+1
3)、a[i-p]
4)、j<=end2
5)、i(或 start2,或 end1+1)
6)、j-1

 

T132
1)、j-1
2)、cur1
3)、count1--
4)、count2--
5)、cur1=a[j]


T141
1)、n
2)、0
3)、stack2[top2]=stack1[top1]
4)、stack1[top1]=stack2[top2]
5)、top1-1

T151
1)、rmax[n-1]=x[n-1]
2)、rmax[i]=x[i]
3)、rmax[i]=rmax[i+1]+x[i]
4)、rmax[i]=rmax[i+1]
5)、lmax[i-1]+rmax[i+1]

T152
1)、v=-1
2)、dist[i]<dist[v]
3)、v=i
4)、used[v]=1
5)、dist[v]+w[v][i]<dist[i]

T161
1)、i<=j
2)、next[rank[i]]=rank[i+1]
3)、higher=height[next[i]]-height[i]
4)、shorter<higher
5)、previous[next[i]]=previous[i]

T162、
1)、dist[1]=0
2)、dist[x]+weight[j]<dist[point[j]]
3)、visit[x]=0
4)、dist[x]+weight[j]==dist[point[j]]
5)、visit[point[j]]=1