20180908解题报告

高三开学第一场模拟赛,又是ywd从不知道什么地方变出来的题目,没有以前那么毒瘤我这种蒟蒻不配说题目水,但是数据还是有一点难度。不知什么原因,他把手机收了,然而除了有一道题洛谷上有(而且数据弱化了很多)其他的题都不知道是什么玩意。

1. 数制转换 (radix.pas 70分)

有一种数制基数是3,权值可以取-1,0,1,并分别用符号-,0,1表示,如这种数制的101表示的是十进制的10,即1*32+0*31+1*30=10,又如这种数制-0表示十进制的-3,即-1*31+0*30=-3。编程要求给定的整数转换成新数制的数,该书的前面不能有多余的0,如10的新数制表示是101,不要输成0101。

输入格式:文件有一行或多行,每行有一个整数N(-2147483647<=N<=2147483647)。

输出格式:对输入文件的每一行输出一行,该行是输入行的整数的新数制表示。

输入输出样例:

Radix.in

Radix.out

10

-3

101

-0

这道题是这样的,首先一个数x,假如它%3==0,那么他的最后一位一定等于0,假如它%3==1,那么它的最后一位一定等于1,而当他%3===2时,最后一位就是-,如果%3==0||%3==1下一步就是x=x/3;,如果%3==2,那么我们就先把这个数+1(使他变得能够被整除,之前%3==1时之所以不减1是因为并不会改变整除结果),然后再除以3,即x=(x+1)/3;然后每一步再配上输出。

代码如下:

#include<stdio.h>
#include<stdlib.h>
int n;
int dfs(int n)
{
    if(n==1)
      {
      printf("1");
      return 0;
      }
    else if(n==0)
      {
      printf("0");
      return 0;
      }
    else if(n==-1)
      {
      printf("-");
      return 0;
      }
    if(n%3==0)
      {
      dfs(n/3);
      printf("0");
      }
    else if(n%3==1)
      {
      dfs(n/3);
      printf("1");
      }
    else if(n%3==2)
      {
      dfs((n+1)/3);
      printf("-");
      }
    return 0;
}
int dfs1(int n)
{
    if(n==1)
      {
      printf("-");
      return 0;
      }
    else if(n==0)
      {
      printf("0");
      return 0;
      }
    else if(n==-1)
      {
      printf("1");
      return 0;
      }
    if(n%3==0)
      {
      dfs1(n/3);
      printf("0");
      }
    else if(n%3==1)
      {
      dfs1(n/3);
      printf("-");
      }
    else if(n%3==2)
      {
      dfs1((n+1)/3);
      printf("1");
      }
    return 0;
}
int main()
{
    while(scanf("%d",&n)!=EOF)
      {
      if(n>=0)
        dfs(n);
      else {n=-n;dfs1(n);}
      printf("\n");
      }
    return 0;
}

2. 乌龙博士的难题(riverbed.pas 100分)

乌龙博士的研究小组经常要对一段河流进行测量分析。他们从上游开始向下游方向等距离的选择了n(n<=30000)个点测量水位深度,得到一组数据d1,d2,d3,…,dn,回到实验室后乌龙博士要对数据进行分析,试图发掘隐藏在数据背后的规律。最近,乌龙博士发现某种水文现象与河床地势有关,于是他想找出一段河流中最大高低起伏差不超过k(k<=100)的最长一段再做仔细研究。这看似一个复杂的问题,乌龙博士的程序设计水平不行,只好求助于你,并告诉你所有的数据都精确到个位,你能帮帮乌龙博士吗?

输入格式:riverbed.in

输入文件有2行。第1行是整数n和k,分别表示测量点的个数和乌龙博士要求的最大水深差。第2行有n个整数,表示从上游开始依次得到的水位深度d(1<=i<=n, 0<=di<=32767)。

输出格式:riverbed.out

只有1行,是整数m,表示最长一段起伏不超过k的河流长度,用测量点的个数表示。

输入输出样例

Riverbed.in

Riverbed.out

6 2

5 3 2 2 4 5

4

样例提示:从第2个测量点到第5个测量点之间的一段,即5 3 2 2 4 5,起伏最大为4-2=2。  

这个题据说是dp,如果是这样的话我可能会考虑写一个最长不上升子序列然后从头和尾开始扫判断能不能使差小于等于k吧,但是就算这样这个算法的复杂度也是nlogn,我采用线段树加上二分的做法,时间复杂度也是nlogn,虽然常数较大但可以优化(利用一次查询两个结果),陈先生跟我说可以试试单调队列n级别可能更快,然而想了半天没搞懂怎么弄,那么下面来讲解一下线段树的做法。首先建立两棵线段树,一棵查询最大值,一棵查询最小值,那么然后我们枚举i从1到n,完了之后可以使r=n如果query1(1,1,n,i,r)-query(1,1,n,i,r)>k,那么就把r变成二分(这段有点难描述等会看代码好了,具体就是你知道左节点和右节点就可以知道中间,那么你知道中间和左节点就可以知道右节点),如果<=k,r再跳回去,然后看看最后(r-i+1)和max1的大小关系,最后输出max1,讲下我遇到的问题,结构体开小了吃了我20分,max1初值应该赋成1吃了我20分,最后一个操作最神秘,是这样的,假如左节点1,中间为3,右节点可能为5或6,开始忽略了这个6导致会少算(这段很难言语描述稍微想想)对了,韩哥哥号称要拿堆做最后崩了

附上代码:

#include<stdio.h>
#include<stdlib.h>
struct segmax{
    int val;
}segmax[500005];
struct segmin{
    int val;
}segmin[500005];
int a[30005]={0};
int max(int x,int y)
{
    if(x>y)
      return x;
    else return y;
}
int min(int x,int y)
{
    if(x>y)
      return y;
    else return x;
}
int build1(int root,int istart,int iend)
{
    int mid;
    if(istart==iend)
      {
      segmax[root].val=a[istart];
      return 0;
      }
    mid=(istart+iend)/2;
    build1(root*2,istart,mid);
    build1(root*2+1,mid+1,iend);
    segmax[root].val=max(segmax[root*2].val,segmax[root*2+1].val);
    return 0;
}
int build2(int root,int istart,int iend)
{
    int mid;
    if(istart==iend)
      {
      segmin[root].val=a[istart];
      return 0;
      }
    mid=(istart+iend)/2;
    build2(root*2,istart,mid);
    build2(root*2+1,mid+1,iend);
    segmin[root].val=min(segmin[root*2].val,segmin[root*2+1].val);
    return 0;
}
int quire1(int root,int nstart,int nend,int qstart,int qend)
{
    int mid;
    if(qstart>nend||nstart>qend)
      return 0;
    if(qstart<=nstart&&qend>=nend)
      return segmax[root].val;
    mid=(nstart+nend)/2;
    return max(quire1(root*2,nstart,mid,qstart,qend),quire1(root*2+1,mid+1,nend,qstart,qend));
}
int quire2(int root,int nstart,int nend,int qstart,int qend)
{
    int mid;
    if(qstart>nend||nstart>qend)
      return 2147483645;
    if(qstart<=nstart&&qend>=nend)
      return segmin[root].val;
    mid=(nstart+nend)/2;
    return min(quire2(root*2,nstart,mid,qstart,qend),quire2(root*2+1,mid+1,nend,qstart,qend));
}
int main()
{
    int n,k,i,j,l,r,max1=1,mid,u,x,y;
    scanf("%d%d",&n,&k);
    for(i=1;i<=n;i++)
      scanf("%d",&a[i]);
    build1(1,1,n);
    build2(1,1,n);
    for(i=1;i<=n;i++)
      {
      l=i;r=n;mid=0;
      if(n-i+1<=max1)
        break;
      while(l<r)
        {
        if(quire1(1,1,n,i,r)-quire2(1,1,n,i,r)<=k)
          {
          if(r-i+1>=max1)
          {
            max1=r-i+1;
          }
          if(r==n)
            break;
          else {
            u=r;
            r=(l+2*(r-l)+r)/2+1; 
            l=u;
            }
          }
        else
           r=(l+r)/2;
        }
      }
    printf("%d",max1);
    return 0;
}

3.  01象素图(bit.pas)

问题描述

古老的显示屏是由N×M个象素(Pixel)点组成的。一个象素点的位置是根据所在行数和列数决定的。例如P(2,1)表示第2行第1列的象素点。

那时后,屏幕只能显示黑与白两种颜色,人们用二进制0和1来表示。0表示黑色,1表示白色。当计算机发出一个指令:P(x,y)=1,则屏幕上的第x行第y列的阴极射线管就开始工作,使该象素点显示白色,若P(x,y)=0,则对应位置的阴极射线管不工作素点保持黑色。

在某一单位时刻,计算机以N×M二维01矩阵的方式发出显示整个屏幕图象的命令。
D(P1,P2)=|x1-x2|+|y1-y2|由于未知的原因,显示黑色的象素点总是受显示白色的象素点的影响——可能是阴极射线管工作的作用。并且,距离越近,影响越大。这里的距离定义如下:设有象素点P1(x1,y1)和象素点P2(x2,y2),则它们之间的距离D(P1,P2):

在某一时刻,计算机发出显示命令后,科学家们期望知道,每个象素点和其最近的显示白色的象素点之间的最短距离是多少——科学家们保证屏幕上至少有一个显示白色的象素点。

上面的例子中,象素P(1,1)与最近的白色象素点之间的距离为3,而象素P(3,2)本身显示白色,所以最短距离为0。

 

输入文件

输入文件名:BIT.IN

第一行有两个数字,N和M (1<=N,M<=182),表示屏幕的规格。

以下N行,每行M个数字,0或1。为计算机发出的显示命令。

 

输出文件

输出文件名:BIT.OUT

输出文件有N行,每行M个数字,中间用1个空格分开。第i行第j列的数字表示距象素点P(i,j)最近的白色象素点的最短距离。

样例输入

3 4

0001

0011

0110

 

样例输出

3 2 1 0

2 1 0 0

1 0 0 1

没错这道就是原题,https://www.luogu.org/problemnew/show/P2335,尽管看起来洛谷这道题数据规模更大但事实上洛谷数据不知道水成什么样了,这个题我还是年轻,写了个枚举黑白点,以为不会超但还是tle了,后排大佬打了个剪枝的bfs(我到现在还没想出来怎么剪枝呜呜呜人家才高二就这么有灵性了,正如一句话,如果一个人比你小还比你强,那你就永远比不过他了,我果然还是一个蒟蒻)。附上丢人代码:

//这已经是我最不丢人的80分代码了,cena评测这题好像tle的更多
#include<math.h>
#include<stdio.h>
#include<stdlib.h>
int dp[201][201]={0},visx[100001]={0},visy[100001]={0},w[201][201]={0};
int main()
{
    int n,m,i,j,k,tmp=0,tmp2=0;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
      for(j=1;j<=m;j++)
        {
        scanf("%1d",&w[i][j]);
        if(w[i][j]==1)
          {
          tmp++;
          visx[tmp]=i;
          visy[tmp]=j;
          }
        }
    for(i=1;i<=n;i++)
      for(j=1;j<=m;j++)
        if(w[i][j]==0)
          dp[i][j]=2147483641;
        else dp[i][j]=0;
    for(i=1;i<=n;i++)
      for(j=1;j<=m;j++)
        {
        if(tmp2+tmp==n*m)
          break;
        if(w[i][j]==1)
          continue;
        else
          {
          tmp2++;
          for(k=1;k<=tmp;k++)
            {
            if(dp[i][j]>abs(visx[k]-i)+abs(visy[k]-j))
              dp[i][j]=abs(visx[k]-i)+abs(visy[k]-j);
            if(dp[i][j]==1)
              break;
            }
          }
        }
    for(i=1;i<=n;i++)
       {
       for(j=1;j<=m;j++)
          printf("%d ",dp[i][j]);
       printf("\n");
       }
    return 0;
}

4.《自动对款机》(ban.pas)

问题描述

ByteLand上的每位居民都有一个自己的银行帐号,帐号上记录了居民的存款金额,以硬币为单位。他们可以用自己的帐号在100台自动对款机上进行取款或付款的操作。

这100台自动对款机的编号从0到99。每台对款机都有自己的运行特点。当你在编号为i的对款机上进行操作时,若i是偶数,你将从对款机中获得2i个硬币;若i是奇数,你将付给该对款机2i个硬币。

假设你要从银行取14枚硬币,那么你可以在第4号对款机上操作,获得16枚硬币,接着在第1号对款机上操作,付出2枚硬币,最终就得到了14枚硬币。

假设你要付给银行7枚硬币,那么你可以在第3号对款机上操作,先付出8枚硬币,接着在第0号对款机上操作,取回1枚硬币,最终你付给银行7枚硬币。

注意,由于银行系统的保险措施,每台对款机最多只能被操作一次。

 

现在,L先生将要向银行支取或交纳一定数额的硬币。你能设计出一个方案,对某些对款机进行操作,从而恰好完成L先生的要求。

 

输入文件

输入文件名:BAN.IN

第一行是一个数N(1<=N<=1000),表示测试数据的个数。

以下N行,第i+1行是一个整数Pi,不超过10^30,表示L先生将要向银行支取或交纳硬币的数额。

 

输出文件

输出文件名:BAN.OUT

输出文件有2N行,每两行对应一个测试数据的答案。

第i×2-1行表示L先生从银行支取Pi个硬币的方案;第i×2行表示L先生向银行交纳Pi个硬币的方案。

每个方案占一行,有一系列数组成,每个数表示需要进行操作的对款机编号。

如果一个方案不存在,那么请在该行输出NIE。

 

样例输入

2

7

633825300114114700748351602698

样例输出

4 3 1 0

3 0

NIE

99 3 1

看到这种题目我就想起去年小凯的疑惑然而并没有什么关系,在四道题中这题稍微有点意思,首先他需要一个高精度写这题只剩半小时了想都来不及还写高精度?,其次老谋深算ywd,这玩意确实实际上就是一个-2进制,他在黑板上还有一个-2进制没擦掉,确实没有人看,来不及了暴力搜索20分。

其实这应该是ywd弄出来历来最简单的一套题(以后再也没有ak的机会了呜呜呜)然而我还是做的不是很好,真是老了,要是我现在还和wky,flx他们一个年纪,或许我也能有机会达到韩哥哥的境界。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值