hihoCoder[Offer收割]编程练习赛3题目解析

来源:互联网 发布:单词社交网络 mp4 编辑:程序博客网 时间:2024/05/01 07:22
题目1 : hiho密码
时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
小Ho根据最近在密码学课上学习到的知识,开发出了一款hiho密码,这款密码的秘钥是这样生成的:对于一种有N个字母的语言,选择一个长度为M的单词;将组成这个单词的所有字母按照顺序不重复的写出(即遇到相同字母时跳过);然后将字母表剩下的没有使用过的字母按照顺序在其后进行排列。
如对于有5个字母的hiho语,选择单词1, 2, 2, 4, 3(此处数字表示字母在字母表中的顺序),则秘钥为1,2,4,3,5。
但是有一天小Ho在计算出了秘钥之后,却发现他弄丢了一开始选择的单词,于是他找到了你,希望你能够帮他找到能够生成这个秘钥的最短的单词。
输入
每个输入文件包含单组测试数据。
每组测试数据的第一行为一个正整数N,意义如前文所述。
每组测试数据的第二行为N个正整数,用来描述一个秘钥,其中第i个正整数Ai表示秘钥的第i个字符在字母表中的顺序。
对于100%的数据,满足N<=1000,1<=Ai<=N。
对于100%的数据,满足对于任意1<=i, j<=N,若i≠j,则Ai≠Aj。
输出
对于每组测试数据,输出能够生成输入给出的秘钥的最短的单词(空串不认为是单词)。由于字母表没有给出,所以对于每个字母,输出其在字母表中的顺序即可(用空格隔开)。
样例输入
5
1 2 4 3 5
样例输出
1 2 4
从后向前遍历整个序列,如果前一个数比后一个数大,那么最短的密钥就是从第一个数到前一个数;如果整个序列是递增的,那么最短的密钥就是第一个数。
AC代码:
#include<cstdio>  #include<iostream>  using namespace std;  int main()  {      int N[1010];      int num,pos=0;      scanf("%d",&num);      for(int i=1;i<=num;i++)      {          scanf("%d",&N[i]);      }      for(int i=num;i>=2;i--)      {          if(N[i-1]>N[i])           {              pos=i-1;              break;           }      }      if(pos)      {          for(int i=1;i<=pos;i++)          {              printf("%d ",N[i]);          }          printf("\n");      }      else      {          printf("%d\n",N[1]);      }  }  
题目2 : 机会渺茫
时间限制:5000ms
单点时限:1000ms
内存限制:256MB
描述
小Hi最近在追求一名学数学的女生小Z。小Z其实是想拒绝他的,但是找不到好的说辞,于是提出了这样的要求:对于给定的两个正整数N和M,小Hi随机选取一个N的约数N',小Z随机选取一个M的约数M',如果N'和M'相等,她就答应小Hi。
小Z让小Hi去编写这个随机程序,到时候她review过没有问题了就可以抽签了。但是小Hi写着写着,却越来越觉得机会渺茫。那么问题来了,小Hi能够追到小Z的几率是多少呢?
输入
每个输入文件仅包含单组测试数据。
每组测试数据的第一行为两个正整数N和M,意义如前文所述。
对于40%的数据,满足1<=N,M<=10^6。
对于100%的数据,满足1<=N,M<=10^12。
输出
对于每组测试数据,输出两个互质的正整数A和B(以A分之B表示小Hi能够追到小Z的几率)。
样例输入
3 2
样例输出
4 1
思路很直接,没有什么好说的。
AC代码:
#include<cmath>  #include<cstdio>  #include<vector>  #include<algorithm>  #include<iostream>  using namespace std;  int main()  {      long long int N,M,i;      vector<long long int> N1,M1;      N1.clear();      M1.clear();      cin>>N>>M;      for(i=1;i<=sqrt(N);i++)      {          if(!(N%i))          {              N1.push_back(i);              if(i!=(N/i)) N1.push_back(N/i);          }      }      for(i=1;i<=sqrt(M);i++)      {          if(!(M%i))          {              M1.push_back(i);              if(i!=(M/i)) M1.push_back(M/i);          }      }      sort(N1.begin(),N1.end());      sort(M1.begin(),M1.end());      long long int m=N1.size()*M1.size();      long long int A=m;      long long int B=0;      long long int len=N1.size();      for(i=0;i<len;i++)      {          long long int d=N1[i];          if(binary_search(M1.begin(),M1.end(),d))          {              B++;          }      }      for(i=2;i<=B;i++)      {          while(!(B%i)&&!(A%i))           {              A=A/i;              B=B/i;          }      }      cout<<A<<" "<<B<<endl;  }  
题目3 : 智力竞赛
时间限制:5000ms
单点时限:1000ms
内存限制:256MB
描述
小Hi、小Ho还有被小Hi强拉来的小Z,准备组队参加一个智力竞赛。竞赛采用过关制,共计N个关卡。在第i个关卡中,小Hi他们需要获得Ai点分数才能够进入下一关。每一关的分数都是独立计算的,即使在一关当中获得超过需要的分数,也不会对后面的关卡产生影响。
小Hi他们可以通过答题获得分数。答对一道题获得S点分数,答错一道题获得T点分数。在所有的N个关卡中,小Hi他们一共有M次答题机会。在每个关卡中,都可以在累计答题次数不超过M的情况下使用任意次的答题机会。
那么现在问题来了,对于给定的N、M、S、T和A,小Hi他们至少需要答对多少道题目才能够完成所有的关卡呢?
输入
每个输入文件包含多组测试数据,在每个输入文件的第一行为一个整数Q,表示测试数据的组数。
每组测试数据的第一行为四个正整数N、M、S和T,意义如前文所述。
第二行为N个正整数,分别表示A1~AN。
对于40%的数据,满足1<=N,M<=100。
对于100%的数据,满足1<=N,M<=1000,1<=T<S<=10,1<=Ai<=50。
对于100%的数据,满足1<=Q<=100。
输出
对于每组测试数据,如果小Hi他们能够顺利完成关卡,则输出一个整数Ans,表示小Hi他们至少需要答对的题目数量,否则输出No。
样例输入
1
2 10 9 1
12 35 
样例输出
5
动态规划,比较简单。从比赛的结果来看通过的人数明显较多。
AC代码:
#include<cstdio>  #include<climits>    #include<cstring>    #include<iostream>    #define MAXN 1010    #define MAXM 1010   using namespace std;    int dp[MAXN][MAXM];   //dp[i][j]表示过前i关答对j题时最少要答错多少道题  int a[MAXN];    int n,m,s,t;        int main()  {        int Q,k,p;        scanf("%d",&Q);        while(Q--)      {            memset(dp,0,MAXM*sizeof(int));            memset(a,0,sizeof(a));            scanf("%d%d%d%d",&n,&m,&s,&t);            for(int i=1;i<=n;i++)          {                scanf("%d",&a[i]);                fill(dp[i],dp[i]+MAXM,INT_MAX);            }            for(int i=1;i<=n;i++)          {                k=(a[i]+s-1)/s;              //第i个关卡最多需要答对几道题                for(int j=0;j<=m;j++)              {                    for(int h=0;h<=k;h++)                  {                        p=a[i]-h*s;                        p=(p<=0?0:((p+t-1)/t));                      //p表示在答对h道题的情况下需要答错几道题                        dp[i][j+h]=min(dp[i][j+h],dp[i-1][j]+p);                    }                }            }            bool has=false;            for(int i=0;i<=m;i++)          {                if(i+dp[n][i]<=m)              {                    has=true;                    printf("%d\n",i);                   break;                }            }            if(!has) printf("No\n");      }    }    
题目4 : 子矩阵求和
时间限制:20000ms
单点时限:2000ms
内存限制:256MB
描述
给定一个无限矩阵A,如下图所示,其中Aij=min(i,j)。  
1 1 1 1 1 1 1 1 1
1 2 2 2 2 2 2 2 2
1 2 3 3 3 3 3 3 3
1 2 3 4 4 4 4 4 4
1 2 3 4 5 5 5 5 5  ... y
1 2 3 4 5 6 6 6 6
1 2 3 4 5 6 7 7 7
1 2 3 4 5 6 7 8 8
1 2 3 4 5 6 7 8 9
      ⋮           ⋱
      x
小Hi希望你从中找到一个N*M的子矩阵,使得子矩阵中元素的和是K的整数倍。  
如果不存在这样的子矩阵,输出-1;否则输出子矩阵左上角元素的下标x和y。如果存在多个满足条件的子矩阵,输出x+y最小的一个;如果仍有多个,输出其中x最小的一个。  
输入
输入的第一行是一个整数Q (1 <= Q <= 100)表示输入数据的组数。
对于每组数据,输入一行三个整数N, M, K。1 <= N, M <= 105, 1 <= K <= 106。
输出
对于每组数据输出一行,-1或者子矩阵左上角元素的下标x和y。
样例输入
2
2 2 10
3 3 31
样例输出
2 3
6 7
这种压轴的题,没有什么套路,考智商考思维。
我们设Sij表示左上角是Aij的子矩阵的和。 
那么有两个重要的性质: 
1. Sij + N × M = Si+1,j+1 
2. 当y>=n时,S1,y=S1,y+1;当x>=m时,Sx,1=Sx+1,1
根据性质1,我们只要知道S11的值,那么S11,S22,S33...这条对角线上的子矩阵和哪些是K的倍数可以O(1)求出。只要解一个同余方程S11+xnm=0 mod K。同理知道S12可以得到S23,S34,S45...
根据性质2,我们只要求S11,S12,S13...S1n和S21,S31,S41...Sm1这O(N+M)条对角线上哪些子矩阵是K的倍数就行。
我的代码TLE了,大家可以参考下,有什么改进的地方希望能不吝赐教。解同余方程的扩展欧几里得算法这里就不再讲解了。
#include<cmath>  #include<cstdio>  #include<cstring>    #include<iostream>    #define MAXN 100010    #define MAXM 100010   using namespace std;    int N,M,K;  int sum1[MAXN];  int sum2[MAXM];    //扩展欧几里得算法   int exgcd(int m,int n,int &x,int &y)  {      int x1,y1,x0,y0;      x0=1; y0=0;      x1=0; y1=1;      x=0; y=1;      int r=m%n;      int q=(m-r)/n;      while(r)      {          x=x0-q*x1; y=y0-q*y1;          x0=x1; y0=y1;          x1=x; y1=y;          m=n; n=r; r=m%n;          q=(m-r)/n;      }      return n;  }    //ax与b关于n同余,解x   int modular_linear_equation(int a,int b,int n)  {      int x,y,x0;      int d=exgcd(a,n,x,y);      if(b%d) return -1;      x0=x*(b/d)%n;        for(int i=0;i<d;i++)       {          int res=(x0+i*(n/d))%n;          if(res>=0) return res;      }  }    //求第i行前M个元素的和   int row(int i)  {      int res=0;      if(i>=M)      {          int j=M;          while(j)          {              res+=j;              j--;          }      }      else      {          int j=1;          while(j<=i)          {              res+=j;              j++;          }          res+=i*(M-i);      }      return res;   }    //求第i列前N个元素的和   int col(int i)  {      int res=0;      if(i>=N)      {          int j=N;          while(j)          {              res+=j;              j--;          }      }      else      {          int j=1;          while(j<=i)          {              res+=j;              j++;          }          res+=i*(N-i);      }      return res;   }    int main()  {      int Q;      cin>>Q;      while(Q--)      {          memset(sum1,0,sizeof(sum1));          memset(sum2,0,sizeof(sum2));          cin>>N>>M>>K;          int tempn=N;          int tempm=M;           int minx=999999;          int miny=999999;          int i=1;          while(tempn&&tempm)          {              sum1[1]+=i*(tempm+tempn-1);              i++;              tempn--;              tempm--;           }          sum2[1]=sum1[1];          for(i=2;i<=N;i++)          {              sum1[i]=sum1[i-1]+col(i+M-1)-col(i-1);          }          for(i=2;i<=M;i++)          {              sum2[i]=sum2[i-1]+row(i+N-1)-row(i-1);          }          for(i=1;i<=N;i++)          {              int res=modular_linear_equation(N*M,abs(sum1[i]-K),K);              if(res>0)              {                  if(1+res+i+res<minx+miny)                  {                      minx=1+res;                      miny=i+res;                  }                  else if(1+res+i+res==minx+miny)                  {                      if(1+res<minx)                      {                          minx=1+res;                          miny=i+res;                      }                  }              }          }          for(i=1;i<=M;i++)          {              int res=modular_linear_equation(N*M,abs(sum2[i]-K),K);              if(res>0)              {                  if(i+res+1+res<minx+miny)                  {                      minx=i+res;                      miny=1+res;                  }                  else if(i+res+1+res==minx+miny)                  {                      if(i+res<minx)                      {                          minx=i+res;                          miny=1+res;                      }                  }              }          }          if(minx==999999) cout<<"-1"<<endl;          else cout<<minx<<" "<<miny<<endl;      }  }  


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 发现自己读初中的儿子偷钱怎么办? 做错事了得不到亲人的原谅怎么办? 窗口 窗套与墙缝隙大怎么办 中班小孩还不会认1到10怎么办 母猫奶头被小猫咬伤了怎么办 口红不小心弄到衣服上怎么办 脖子后背疼的睡不着觉应该怎么办 君子兰用高锰酸钾泡浓度高了怎么办 五个月宝宝认人不要奶奶睡怎么办 幼儿小班安全卡鱼刺了怎么办教案 学籍在一年级学生在二年级怎么办 一岁多宝宝挑食不爱吃饭菜怎么办饭 换了新手机微信好友显示不全怎么办 朋友人在外地 联系不上怎么办 幼儿连字母都记不住该怎么办 车一边轮子掉农村路边小水沟怎么办 20个月小孩脾气急燥怎么办 给小孩上户口母亲是外国人怎么办 宝宝在学校不敢跟老师说话怎么办? 早教课上宝宝总爱乱跑怎么办? 20天大的宝宝4天不拉大便怎么办 还不会说话的小孩子脾气大怎么办 两岁的宝宝说话变的口吃怎么办 37周b超宝宝腿短怎么办 3岁的宝宝说话说不好怎么办 小儿说话晚的原因宝宝说话晚怎么办 22岁的儿子在家不说话怎么办 儿子2周4个月了不说话怎么办 三岁的宝宝还不会数数怎么办 2岁宝宝晚上不睡觉不听话怎么办 8个月的狗狗怕人不敢出门怎么办 狗太怕人了怎么办都不敢出门 媳妇和婆婆晚上都不想带孩子怎么办 婆婆和媳妇带孩子观念不一样怎么办 婆婆给媳妇买了不喜欢的家具怎么办 断奶后宝宝对奶粉很抗拒怎么办 两岁宝宝断奶不喝奶粉怎么办 宝宝两岁了断奶后奶粉不吃怎么办 宝宝断奶两天了不愿意吃奶粉怎么办 一岁四个月宝宝断奶不喝奶粉怎么办 四个月宝宝断奶不喝奶粉怎么办