30 条题解
-
49
数学解法秒杀,跟递归没什么关系 但是考虑到一部分人的需要,我给一下递归算法吧
递归函数
int f(int n){ if(n<=2)return 0; return func((n-1)/2)+1;
然后你可以用二分查找定位最大的n使得f(n)==x,记得n用long long类型
那么,既然递归算法都想到了,尝试着使用更加简洁易懂的代码完成题目吧:
对于一段长度为n的绳子,对它分割产生长度分别为a,b,c的三段,由题a,b,c≥1,不妨设a≤b≤c,则我们最终会保留长度为b的那一段
对于长度为p的绳A与长度为p+1的绳B,对绳A的每一种分割方式 A={A[1],A[2],A[3]},其中A[1]≤A[2]≤A[3], 我们可以对绳B作如下分割:B={A[1],A[2],A[3]+1} 则此时以此为分割方式的A的最大分割次数f(A)=f(A[2])+1 B的最大分割次数f(B)=f(A[2])+1 =f(A) 此外我们有时还可以对绳B作一些无法对绳A作的分割f(B) 令F(p)={x|x=f(A)},F(p+1)={x|x=f(B)},则根据以上有
F(p)⊆F(p+1)
又因为f(p)为F(p)的最大元,f(p+1)为F(p+1)的最大元,有
f(p)≤f(p+1)
因此,为了产生最大化的分割次数,我们需要使每一次分割后留下的绳子长度最大化,即
使b最大
我们需要在保持a≤b≤c的前提下使得b取最大值 ∵a+b+c=n,a≥1,b≤c ∴b=(b+b)/2≤(b+c)/2=(n-a)/2≤(n-1)/2 其中第一个等号在b=c时取得,第二个在a=1时取得 总结一下,即
b=[(n-1)/2]
此时f(n)=f(b)+1=f([(n-1)/2])+1 用m=[(n-1)/2]替换,得
f(2m+2)=f(m)+1
f(2m+3)=f(m+1)+1
则对f(r)=k-1,f(r+1)=f(s)=k,f(s+1)=k+1,有
s=2r+2
设使f(n)=x的最大n为g[x],则有
g[x]=2·g[x-1]+2
对t≤2,我们知f(t)=0,同时f(3)=f(1)+1 =1 则使f(t)=0的最大t为2,即
g[0]=2
因此我们写下新的递归代码
long long g(int n){ if(n==0)return 2; return 2*g(n-1)+2; }
整理递归代码,得到递推思想的简单代码
int main(){ long long x=2; int n; cin>>n; for(int i=1;i<=n;i++)x=2*++x; cout<<x; return 0; }
但是这还不够!我们先前只得到了数列g的递推公式,还没有得到通项公式呢! 所以我们借助我们的数学功底,得到通项公式:
g[x]=Σ(下标i=1,上标x+1)2^i
再进行整理,得
g[x]=2^(x+2)-2
于是我们可以用以下的代码解决:
#include<iostream> using namespace std; int main(){ int x; cin>>x; cout<<(1<<x+2)-2; return 0; }
理论上这段代码是正确的,但是实际上会数据溢出😕 所以会有两个WA(用long long类型也不行,是左移运算符的缺陷导致的)
因此我们得到最终的代码:
AC代码
int main(){ long long x=2; int n; cin>>n; for(int i=1;i<=n;i++)x=2*++x; cout<<x; return 0; }
左移简便代码(非AC)
#include<iostream> using namespace std; int main(){ int x; cin>>x; cout<<(1<<x+2)-2; return 0; }
制作不易,给个点赞吧
-
17
P1006 【挑战题】分割
题目描述
有一段长度为正整数的绳子,将以下的操作持续到绳长变为2或更小:选择两个位置将绳子截成长度为正整数的三段,丢弃其中最长的一段和最短的一段。
当对长度为n的绳子进行此操作时,令f(n)为该操作可进行的最多次数。
给出正整数x,求使f(n)=x的最大整数n。
逆向思考,他给次数?我们从二开始倒退!
根据题意,为了让操作次数尽量多,每次分割绳子的时候,需要让被留下的那一段尽量长,那么:
最短的那段长度一定是1,剩下两段一定是尽量平均分的,也就是说这两段相差不会超过1。
比如,当前长度是8,那么分成1,3,4三段,此时剩下的3是最长的。
按照此规则,假设某次分割后剩下的长度是n,则本次分割出的三段分别是1,n,n+1,总和2n+2。
因此,从n=2开始,每次根据当前的n,求出分割前的长度2n+2,重复x次即可求出
(这里引用一下@hetaowjl(SU)的题解,在此表示感谢)
参考代码:
#include <iostream>//hetao3097453 using namespace std; int main() { int n; cin >> n; int x = 2; for(int i = 1;i <= n;i++) { x = 2 * x + 2; } cout << x << endl; return 0; }
-然鹅:
…………………………………………………………
所以,用
long long
一定不会错#include <iostream>//hetao3097453 using namespace std; int main() { long long n; cin >> n; long long x = 2; for(int i = 1;i <= n;i++) { x = 2 * x + 2; } cout << x << endl; return 0; }
hetao3097453(blililil @ 一钩出站)
2023年3月10日
-
11
这构思玩意能AC??!!
#include<iostream> using namespace std; #define POPOPOPOPOPOPOPOPOPOPOPOPOOPOPLOL return #define QWERTYUIO_____________________________ for #define ______________________________________________ int #define IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIllllllllllllllllllllllllllllllllllIIIIIIIIIIIIIIIIIIIIIIIIII long long ______________________________________________ main() { IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIllllllllllllllllllllllllllllllllllIIIIIIIIIIIIIIIIIIIIIIIIII x; cin>>x; IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIllllllllllllllllllllllllllllllllllIIIIIIIIIIIIIIIIIIIIIIIIII n=2; QWERTYUIO_____________________________(______________________________________________ i=0;i<x;i++) { n=2*n+2; } cout<<n; POPOPOPOPOPOPOPOPOPOPOPOPOOPOPLOL 0; }
-
4
这一题最重要的就是找到规律(就是规律有点难找……)
#include <bits/stdc++.h> using namespace std; long long f(long long x) //不带把数据开得这么大的…… { if (x == 1) return 6; //递归边界 return 2 * f(x - 1) + 2; //规律(这一次的结果=2×上一次的结果+2) } int main() { int x; cin >> x; cout << f(x); //我个人还是比较习惯用递归 return 0; }
不习惯用递归的↓
#include <bits/stdc++.h> using namespace std; int main() { long long x, ans=2; cin >> x; for (int i = 1; i <= x; i++) //和递归函数一个作用 ans = 2 * ans + 2; cout << ans; return 0; }
两种代码都试过了,皆AC
-
4
提示1
本题不需要使用递归。
提示2
需要逆向思考这个问题(从2开始不断把绳子变长)。
提示3
每次舍掉的最短的那一段,长度一定是1。
完整思路
题目求的是最大的n,那么为了让n尽量大,应该让绳子被操作x次后,最终得到2(而不是得到1)。 反过来考虑,也就是从2开始,复原x次,最终得到n。 根据题意,为了让操作次数尽量多,每次分割绳子的时候,需要让被留下的那一段尽量长,那么: 1. 最短的那段长度一定是1。 2. 剩下两段一定是尽量平均分的,也就是说这两段相差不会超过1。 比如,当前长度是8,那么分成1,3,4三段,此时剩下的3是最长的。 按照此规则,假设某次分割后剩下的长度是n,则本次分割出的三段分别是1,n,n+1,总和2n+2。 因此,从n=2开始,每次根据当前的n,求出分割前的长度2n+2,重复x次即可求出答案。
-
0
讲一下大致思路:首先,函数定义一定用long long ,int只能达到2的31次方,而本题最大可将近2的42次方;其次,找到相邻两项之间的关系:当x为1时,结果为6,当x为2时,结果为14......大家可能看出来了,第2项为前1项*2+2;原理:我们最好能够将最短的绳子为1,最长的绳子和第二长的绳子相等(或相差1),注意到,6=1+2+3,14=1+6+7,30=1+14+15......于是就有了下面的函数主体代码:
long long f(int n) { if(n==1) { return 6; } return 2+2*f(n-1); }
【本人B站:冷焰LY111(不是编程,是游戏UP,感兴趣的可以去看)】
-
0
-题目回顾-
有一段长度为正整数的绳子,将以下的操作持续到绳长变为2或更小:选择两个位置将绳子截成长度为正整数的三段,丢弃其中最长的一段和最短的一段。
当对长度为n的绳子进行此操作时,令f(n)为该操作可进行的最多次数。
给出正整数x,求使f(n)=x的最大整数n。
-分析-
最小一段最小为二,我们就取二;而剩下的就是留下的一段*2,即
n * 2
.根据这个,我们就可以退出来通项公式:n = n * 2 + 2
;
代码如下:
#include <bits/stdc++.h>//by AGOMG using namespace std; long n = 2, x; int main() { cin >> x; for(int i = 1; i <= x; i++) n = n * 2 + 2; cout << n; return 0; }
信息
- ID
- 10
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 5
- 标签
- 递交数
- 4776
- 已通过
- 1729
- 上传者