【题解&&反思】海亮信息集训A-B班-分治初步专项







一、山楂关卡
题意简述:

小 VHOS 带着 N 颗山楂球来到 A 班的阵地准备缩到位置上吃山楂球。


但是 A 班大佬们太强了,所以小 VHOS 每经过一个 A 班大佬身边, 他都需要交出他手中一半的山楂(向下取整)


但是 A 班大佬看在小 VHOS 没吃午饭,都会从他们所拿的那一半中额外留出一颗山楂球给小 VHOS。
经过重重的关口,小 VHOS 只剩下 X 颗山楂,他的午饭没了 qwq...小 VHOS 很菜,
他心情慌张,连自己还剩几颗山楂都不知道了 qwq…
所以帮小 VHOS 统计剩余山楂的任务就交给了你。
输入格式:


一行 2 个整数 N, M,表示原先带到 A 班的山楂总数, A 班同学的人数。

输出格式:
一行整数,表示小 VHOS 剩余的山楂球个数


分析:当时我一看这题目。。。大水题啊!!!

         我草我怎么也没想到我文件名打错了。。。这种错误下次也不能再犯了。。丢了30分啊。

         为什么说只丢了30分呢。。因为题目还有坑

          我当时一直以为这个山楂个数是每次除以2+1。

          然而讲评之后我才知道是每次从中取出一半再加一

          (注意上面的两个说法是不一样的)

          。。语文没学好的缘故啊!!

#include<bits/stdc++.h>
using namespace std;
int m,n;
int main(){
 freopen("station.in","r",stdin);
 freopen("station.out","w",stdout);
    scanf("%d%d",&n,&m);
    int x=0;
    while (n!=3&&n!=2&&x<m) n=n-n/2,n++,x++;//注意这里需要加一个小剪纸。因为这里的学生个数可能远远大于山楂个数,所以如果不加剪枝就会超时(当n=3或者n=2时,在怎么取也不会遍)
    printf("%d",n);
    fclose(stdin);
    fclose(stdout);
    return 0;
}


二、跳楼梯

  题意简述:OpportunityFan(以下简称 OF)正在准备体育考试,他为跳远感到忧愁。 VHOS
建议他每天跳 N 级台阶,锻炼爆发力。为了增加虚伪之力, OF 给自己制定了一项规则:
当他跳到偶数级台阶时,他可以跳2 级或 3 级;
当他跳到奇数级台阶时, 他可以跳 1 级或 4 级。
你需要求, 若刚开始时他处在 0 级(属于偶数),他跳到 N 级的方法数量是多少。


分析:很明显的递推题嘛。
          因为题目中走楼梯是分奇偶的,那么我们就需要按奇偶判断:


             1、如果是奇数,那么就可以通过前一次奇数台阶的4步以及前一次偶数台阶的3步跳过来

                     可以得到如下的转移方程:
                  f[i]=f[i-4]+f[i-3]//i%2==1


            2.如果是偶数,那么就可以通过前一次奇数台阶的一步以及前一次偶数台阶的2步跳过来

                  可以得到如下的状态转移方程:
                f[i]=f[i-1]+f[i-2]//i%2==0

那么具体代码如下

#include<bits/stdc++.h>
using namespace std;
int a[50000001];
int n;
const int p=1000000007;
int main(){
 freopen("stairs.in","r",stdin);
 freopen("stairs.out","w",stdout); 
    scanf("%d",&n);
         f[1]=0;
 f[2]=f[3]=1;//初值
    for (int i=4;i<=n;i++)
      if (i%2==0) a[i]+=a[i-2]+a[i-1],a[i]%=p;
      else a[i]+=a[i-3]+a[i-4],a[i]%=p;//如上所述
    printf("%d",a[n]%p);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

      三、外星猫

        题意简述:VHOS 养了一只从外星来的猫, 这只猫的基因由三种字符组成: c, a, t。但这只猫会疯狂进化,每过一个单位时间, 这只猫的基因都会复制一遍,并在原基因和复制出来的基因中加入 t-1 个 a ,组成新基因。当 t=1 时,它的基因为”cat” ;当 t=2 时, 它的基因为“catacat” ;当 t=3 时, 它的基因为“catacataacatacat” 。VHOS 想知道在这只猫的第 i 位的基因是什么,什么时候出现了这位基因


分析:这道题是一个简单的递推和递归(或者分治。但我当时二分写炸了。。一脸蒙蔽。)

         想要解决第二个小问题,我们就需要进行以此递推:

          我们用f[i]表示在第i个单位时间时基因的总长度

              那么我们思考一下:如何递推?

              按照题意,每过一个单位时间,基因都会先产生a-1(a为目前的时间),然后在复制一遍之前的基因。这就是第a分钟产生的基因。

       那么我们可以得到以下递归式:

a[1]=3;
    for (int i=2;a[i-1]<300000000;i++) a[i]=a[i-1]*2+i-1;

在解决第一个问题

根据题意,基因会分成三个部分:前半部分,中间一坨a,后半部分

因为后半部分与前半部分是相等的,所以如果问到后半部分某个字母就等于等量代换到前面的某个字母。在进行递归

那么具体代码如下:

#include<bits/stdc++.h>
using namespace std;
int a[10001]={};
int n;
char dfs(int k){
    if(k<=3) return k==1?'c':k==2?'a':'t';//如果小于3,直接返回 
    int sum=1;
    for (;k>a[sum];sum++);//寻找基因时间(所在区间)
 sum--;//前一个
 if (a[sum]+sum>=k) return 'a';//所在的是a的区间 
 else dfs(k-a[sum]-sum);//否则查询右区间 
}
int main(){
 freopen("cat.in","r",stdin);
 freopen("cat.out","w",stdout);
    scanf("%d",&n);
    a[1]=3;
    for (int i=2;a[i-1]<300000000;i++) a[i]=a[i-1]*2+i-1;
    while (n--){
     int x;
     scanf("%d",&x);
     int ans=0;
     for (;a[ans]<x;ans++);//查询时间 
     printf("%c %d\n",dfs(x),ans);
 }
 fclose(stdin);
 fclose(stdout);
 return 0;
}

四、朝三暮四  


       题意简述:朝三暮四是一个大家耳熟能详的故事, 当 VHOS 从虚伪大魔王的城堡逃出来时,顺了
m 只相同的猴子出来(是不是很神奇?)。
可现在有一个问题: 猴子们饿了。
VHOS 有 n 个相同的栗子,他在给猴子分栗子时,一定要满足每个猴子都有栗子,他
一共有多少种分栗子的方法(对 1×109+7 取模)。

分析:因为数据很水原来本事递推的题目硬生生的能被递归水过了。(虽然我还是递推。)

         递归思想如下:因为n是大于m的,那么肯定每个猴子都会有一个栗子

                               我们先给每个猴子都分一个栗子,那么问题就转换为n-m有多少种拆分方案(具体自己思考为什么)

                               那么这道题就真真正正的变成了一道递归的大水题啊!直接递归n-m的拆分方案便可


        递推的思路如下:我们用一个f[i][j]表示i只猴子吃j个栗子的最优方案

                                我们还是那个思路,将原问题转化为n-m有多少种拆分方案(只是一种类似的思路)。那么我们观察后发现,其实x只猴子就是用x个数对原数进行拆分,即将原数拆分成x个数的和的方案数。

                               可以得到一下递推式:

for (int i=1;i<=n;i++) f[i][i]=1,f[1][i]=1;
    for (int i=2;i<=m;i++)//枚举猴子数 
      for (int j=i;j<=n;j++)//枚举栗子数 
   for (int k=1;k<=i;k++)//枚举之前猴子数 
           f[i][j]+=f[k][j-i],f[i][j]%=p;

具体代码如下

#include<bits/stdc++.h>
using namespace std;
int f[101][101]={};//i只猴子j分h个栗子的方案数 
int m,n;
const int p=1000000007; 
int main(){
 freopen("monkey.in","r",stdin);
 freopen("monkey.out","w",stdout);
    scanf("%d%d",&n,&m);
    if (n<m){printf("0");return 0;}
    for (int i=1;i<=n;i++) f[i][i]=1,f[1][i]=1;
    for (int i=2;i<=m;i++)//枚举猴子数 
      for (int j=i;j<=n;j++)//枚举栗子数 
   for (int k=1;k<=i;k++)//枚举之前猴子数 
           f[i][j]+=f[k][j-i],f[i][j]%=p;
    printf("%d",f[m][n]%p);
    fclose(stdin);
    fclose(stdout);
    return 0;
}






























其实我们这次考试还有第五题。

但因为我听说第五题是神奇的RMQ算法(似乎是树状数组把。。)。暂时还没写出,这里就不发了











反思:


    1、千万要给自己的考试留时间检查头文件啊啊啊啊!

   2、当数组开太大时千万不要直接赋值啊啊啊啊啊!

    3、千万要把题目看仔细啊啊啊啊啊!

  这些都是血与泪的教训啊啊啊啊啊!






   















猜你喜欢

转载自blog.csdn.net/huang_ke_hai/article/details/81041757
A-B
今日推荐