一、山楂关卡
题意简述:
小 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、千万要把题目看仔细啊啊啊啊啊!
这些都是血与泪的教训啊啊啊啊啊!