翻车了,上次装大了,这次来了最不拿手的。。。。递推(下次是更不拿手的搜索~~)
—————————————————————垃圾题目啊—————————————————————
没毛病,开始讲了~~
第一题
Hawthorn Station(station.cpp)
题目描述:
cz 带着 N 颗山楂球来到 A 班的阵地准备缩到位置上吃山楂球。但是 A 班大佬们太强了,所以cz 每经过一个 A 班大佬身边,他都需要交出他手中一半的山楂(向下取整);但是 A 班大佬看在cz没吃午饭,都会从他们所拿的那一半中额外留出一颗山楂球给cz。经过重重的关口,小 VHOS 只剩下 X 颗山楂,他的午饭没了 o(╥﹏╥)ocz很菜,他心情慌张,连自己还剩几颗山楂都不知道了 o(╥﹏╥)o所以帮cz 统计剩余山楂的任务就交给了你。
输入格式:一行 2 个整数 N,M,表示原先带到 A 班的山楂总数,A 班同学的人数。
输出格式:一行整数,表示cz剩余的山楂球个数。
样例数据:
Sample Input
8 1
Sample Output
5
数据规模与运行资源限制
对于 30%的数据,N≤10,M≤3;
对于 80%的数据,N≤1×105,M≤20;
对于 100%的数据,N≤1×107,M≤2×10^9;
时间限制:1s
空间限制:256MB
分析 :cz好可怜啊~~
看到这个大大的提示红字了吗?坑在这里呀~~
退出n=n-(n>>1)+1;
但 注意这里 M 较大,如果使用无脑递推会被卡掉,我们必须考虑剪枝;观察到 N 远远小于 M,那么易得一点:最后的山楂数一定维持在常数上;那么就是,若山楂数只有 2 或者 3,那么山楂数再也不会因为 A 班同学的掠夺而减少
所以代码还是简单的:(但细节还是算多了)
#include
using namespace std;
int n,m,i;
inline const int read() //快读,我已经因此着迷了~~
{
int num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9')
{
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9')
{
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}
int main()
{
freopen("station.in","r",stdin);
freopen("station.out","w",stdout);
n=read(),m=read();//读入
for(i=1;i<=m;i++)//开始我的递推
{
n=n-n/2+1;//是减哦,o(╥﹏╥)o因为是交出且向下取整嘛~~
if(n==2)//特判断 2,不然肯定超时的了
{
printf("%d",2);
return 0;
}
if(n==3)//因为是交出,所以还要特判3
{
printf("%d",3);
return 0;
}
}
printf("%d",n);//数据小?输出
fclose(stdin);
fclose(stdout);
return 0;
}
第二题喽
第二题
跳楼_梯(stairs.cpp)
题目描述
cz正在准备体育考试,他为跳远感到忧愁。ltz建议他每天跳 N 级台阶,锻炼爆发力。为了增加虚伪之力,cz 给自己制定了一项规则:
当他跳到偶数级台阶时,他可以跳 2 级或 3 级;
当他跳到奇数级台阶时,他可以跳 1 级或 4 级。
你需要求,若刚开始时他处在 0 级(属于偶数),他跳到 N 级的方法数量是多少。
输入格式一行,一个整数,表示 N。
输出格式一行,一个整数,表示答案。最后的答案 mod 1×109+7。
样例数据
Sample Input
9
Sample Output
4
数据规模与运行资源限制
对于 20%的数据N ≤ 10
对于 80%的数据N ≤ 1 × 10^6
对于 100%的数据N ≤ 5 × 10^7
时间限制:1s
空间限制:512MB
分析:太限制了,弄得我规律找错了(只有15分。。。。)
1)当 i 为奇数,那么 i-1,i-2,i-3,i-4 为偶数,奇数,偶数,奇数,在奇数时,我们可以排除 i-3,i-4 两种不合法的状态转移;
2)当 i 为偶数,那么 i-1,i-2,i-3,i-4 为奇数,偶数,奇数,偶数,在偶数时,我们可以排除 i-1,i-2 两种不合法的状态转移;
那么这道题的状态转移式是:
dp[i]=dp[i-1]+dp[i-2]---i%2==0;
dp[i]=dp[i-3]+dp[i-4]---i%2==1;
代码:
#include
using namespace std;
int n,i,f[50000005];
long long MOD=pow(10,9)+7; //防止少0,嗯,这样定吧,意思就是10^9+7
inline const int read() //快读(还是每次都注释一遍吧
{
int num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9')
{
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9')
{
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}
int main()
{
freopen("stairs.in","r",stdin);
freopen("stairs.out","w",stdout);
f[0]=0;f[1]=0;f[2]=1;f[3]=1;//赋初值,这里有个细节,如果在头文件那赋值,编译会非常非常满,从而爆0,而且内存已经是10^9多kb了
for(scanf("%d",&n),i=4;i<=n;i++)//赋完初值,才能从4开始,不让心中爆炸
if(i%2==0) f[i]=(f[i-1]+f[i-2])%MOD;//是偶数
else f[i]=(f[i-3]+f[i-4])%MOD;//是奇数
printf("%d",f[n]%MOD); //答案再mod一下
fclose(stdin);
fclose(stdout);
return 0;
}
浪啊,迎接下一题喽:
第三题
外星猫(cat.cpp)
题目描述:
cz 养了一只从外星来的猫,这只猫的基因由三种字符组成:c,a,t。但这只猫会疯狂进化,每过一个单位时间,这只猫的基因都会复制一遍,并在原基因和复制出来的基因中加入 t-1 个 a ,组成新基因。
当 t=1 时,它的基因为”cat”;
当 t=2 时,它的基因为“catacat”;
当 t=3 时,它的基因为“catacataacatacat”。
cz 想知道在这只猫的第 i 位的基因为什么,什么时候出现了这位基因。
输入格式:第一行一个数 t,表示问题个数;第 2 到 t+1 行,每行一个整数 i。
输出格式:t 行每行第一个为字符,表示这只猫的第 i 位的基因;第二个为一个整数,表示出现这位基因的时间。
样例数据Sample Input
3
7
13
17
Sample Output
t 2
a 3
a 4
数据规模与运行资源限制0 < t ≤ 10
对于 20%的数据0 < i ≤ 40
对于 50%的数据0 < i ≤ 10000
对于 100%的数据0 < i ≤ 3 × 10^8
时间限制:1s
空间限制:256MB
分析:
dp[i]表示,在 i 时刻我们的外星猫基因扩展到的大小
dp[i]=dp[i-1]*2+i-1;
然后分类讨论:
1)问题基因在前半段中;2)问题基因在那堆中间插入的 a 里面;3)问题基因在后半段中;对于每个问题分类求解即可;
1)对于情况 1),进入递归查询前一组基因,直到只剩下 3 个基因;
2)对于情况 2),返回输出‘a’;3)对于情况
3),减去偏移量后等同于情况 1);
代码:
#include
using namespace std;
int len[222]={},i,j,maxx=0,k,m,n;
char c,c1;
inline const int read() //快读
{
int num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9')
{
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9')
{
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}
char dfs(int k,int n)//dfs来找c,a,t中的哪一个
{
if(k==1)
return 'c';
if(k==2)
return 'a';
if(k==3)
return 't';
for(int i=n;i>=2;i--)
if(k>=len[i-1]+1)
if(k-len[i-1]<=i-1)
return 'a';
else
return dfs(k-len[i-1]-(i-1),n-1);
}
int main()
{
freopen("cat.in","r",stdin);
freopen("cat.out","w",stdout);
n=read();
len[1]=3;//len1时,cat
for(int i=2;len[i-1]<=300000000;i++)
len[i]=len[i-1]*2+i-1,maxx=max(maxx,i);//递推求出这个len长度的所有
for(i=1;i<=n;i++)
{
k=read();
m=1;
for(j=1;j<=maxx;j++)//找出那个第几次的
if(k<=len[j]&&k>=len[j-1]+1)//是否找到了
{
m=j;
break;
}
c=dfs(k,m);//找字符
cout<
第四题
朝三暮四(monkey.cpp)
题目描述
朝三暮四是一个大家耳熟能详的故事,当 cz 从虚伪大魔王的城堡逃出来时,顺了m 只相同的猴子出来(是不是很神奇?)。可现在有一个问题:猴子们饿了。cz 有 n 个相同的栗子,他在给猴子分栗子时,一定要满足每个猴子都有栗子,他一共有多少种分栗子的方法(对 1×10^9+7 取模)。
输入格式一行两个数 n,m。
输出格式一行一个数表示答案(对 1×10^9+7 取模)。
样例数据
Sample Input
5 4
Sample Output
1
数据规模与运行资源限制0 < m ≤ n ≤ 40
时间限制:1s
空间限制:256MB
分析:
i 表示猴子的编号;j 表示栗子的编号;k 表示这一只猴子至少分配的栗子;
#include
using namespace std;
long long ans=0,n,m,j,k,MOD=pow(10,9)+7;//MOD怕打错
int f[52][52][52];//三维设置
inline const long long read() //快读
{
long long num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9')
{
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9')
{
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}
void work(int x,int y,int c)
{
if(x<0)
return;
if(x=1;i--)
work(x-i,y+1,i);
}
int main()
{
freopen("monkey.in","r",stdin);
freopen("monkey.out","w",stdout);
n=read();m=read();
for(int i=n-m+1;i>=1;i--)
work(n-i,1,i);//调用函数
printf("%d",ans);
fclose(stdin);
fclose(stdout);
return 0;
}
第五题
祭祀咒语(mantra.cpp)
题目描述
ltz最近找到了上古祭师 cz留下的一本祭祀古籍,在cly的帮助下她读懂并学会了某种神奇祭祀方法。首先她需要一个长度为 k 的随机数序列作为咒语的源头,我们称之为源,然后ltz 酱需要找出源序列的所有连续子序列的特定长度的法术值最大的序列并按长度顺序输出。首先我们定义一个源序列的连续子序列(包括它自己)的两个参数:
1)序列中所有元素的最小值为α;
2)序列中所有元素的最大值为β;
我们定义该序列的法术值为α乘以β的乘积值。由于我们的 Menhera 酱还需要为广大表情包事业做出贡献,于是最后一步,提交长度从 1~k 的最大法术值序列的任务就被 ltz扔给了你~
输入格式注意:每个测试点内采用多组数据进行测试;每组数据第一行一个整数 k,源序列的长度;
接下来 k 行,第 i+1 行(i∈Z)一个整数,表示源序列的第 i 个元素 Ai;
输出格式 输出共 k 行,每行一个整数,按从子序列长度 1~k 的顺序输出每种子序列的集合中的法术值最大的那个序列的法术值。
样例数据
Sample Input
5
1
6
2
4
4
Sample Output
36
16
12
12
6
样例解释下标从 1 开始。
长度为 1 的最大法术值区间为 2~2,答案为 6∗6=36;
长度为 2 的最大法术值区间为 4~5,答案为 4∗4=16;
长度为 3 的最大法术值区间为 2~4,答案为 2∗6=12;
长度为 4 的最大法术值区间为 2~5,答案为 2∗6=12;
长度为 5 的最大法术值区间为 1~5,答案为 1∗6=06;
分析:1)递归求解:这道题的关键实际上是随机两个字,VHOS 在造数据时还特别考察了一种优秀的随机数生成算法:梅森旋转算法,这种算法每 2^19937-1 个数字才会出现一个重复,但是我们这里就不论述了,总而言之,我们可爱的递归就不会被卡了,怎么递归求解?我们想到随机就会想到另一种算法:快速排序,它的时间复杂度依赖于划分区间是否均匀;那么我们考虑是否可以设计一种算法利用数据的随机实现均匀的划分,实现期望O(nlogn)的复杂度呢;这里我们考察以下算法:假设:solve(l,r)来求出区间[l,r]中各种区间长度的最大价值。如果在这个区间里一个 for 循环暴力确定最小值是多少。然后我们可以去计算以最小值为中心,左右两边的区间法术值,然后我们就可以更新(注意,是更新而不是确定)整个区间的最大法术值;然后递归调用 solve(l,p-1)和 solve(p+1,r),用同样的方法确定这两个区间里的最小值,然后确定每一个区间段的最大值,再去更新整个区间的最大法术值!9这样,我们就推出了这道题的最优解法,由于这个过程类似与快速排序,所以时间复杂度为期望 O(nlogn),由于是基于随机的算法,所以最坏 O(n^2),可有鉴于题目明确指出数据在统计意义下随机,这个算法完全可以轻松过掉所有数据!2)递推求解这种方法和上面的类似,但是更好想也更快,因为少去了递归的花费;首先,我们用一个 for 循环扫描区间,每次需要假设 A_i 为最小数,然后向外扩展至前面的条件不成立,然后在扫描的过程中反复更新最大法术值;由于数据随机,完全不用担心被卡掉,std 采用这种方法3)RMQ+单调 Stack这个方法比较优秀,因为它是稳定 O(nlogn)的,如果数据不随机,而是有序的,那么这就是最优解法了,实测随机数据下要慢一点;先使用倍增求 RMQ O(nlogn)得到一个 st 表,可以在 O(1)内查询区间最大值,并利用单调栈算出以 input[i]为最小值的最大区间延伸;设该区间宽度为 L,则更新 ans[L](这里用到 st 表查询该区间最大值),最后从宽度较大的区间刷新宽度较小的区间的长度,即为答案。为什么是对的,可以证明一下,从结果考虑,宽度为 k 的区间的最优值一定可以被这种方式取到。本算法复杂度由 RMQ 提供,O(nlogn);
代码要吗?
————————————再见————————————