• 第二场周赛(递归递推个人Rank赛)——题解


    很高兴给大家出题,本次难度低于上一场,新生的六个题都可以直接裸递归式或者裸递推式解决,对于老生的汉诺塔3,需要找出一般式,后两题分别为裸ST算法(或线段树)/线性DP。

    正确的难度顺序为

    1. 种花
    2. 角谷定律
    3. 猴子和椰子
    4. 汉诺塔1
    5. 汉诺塔2
    6. 整数划分
    7. 跳台阶
    8. 汉诺塔3
    9. 夏目友人帐(一)
    10. 夏目友人帐(二)

    一、种花

    本题很容易能推出递推式或者一般式,对于第一快地,有3种种植方法,对于后面的每一快地有不同于前一块地的两种种植方法。

    • a1 = 3;
    • an = 2*(an-1

    代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 int a[25];
     5 
     6 void init(){
     7     a[1] = 3;
     8     for(int i = 2; i <= 20; i++){
     9         a[i] = a[i-1]*2;
    10     }
    11 }
    12 
    13 int main(){
    14     freopen("test.out","w",stdout);
    15     int n;
    16     init();
    17     while(cin>>n){
    18         cout << a[n] << endl;
    19     }
    20     return 0;
    21 }

    二、角谷定律

    本题按照给出的式子操作即可,用另外一个变量记录操作次数。

    代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 int cnt = 0;
     5 void fun(long long n){
     6     if(n == 1)
     7         return ;
     8     if(n&1)
     9         cnt++,fun(3*n+1);
    10     else
    11         cnt++,fun(n/2);
    12 }
    13 
    14 int main(){
    15     int n;
    16     freopen("test.in","r",stdin);
    17     freopen("test.out","w",stdout);
    18     while(cin>>n){
    19         cnt = 0;
    20         fun(n);
    21         cout << cnt << endl;
    22     }
    23     return 0;
    24 }

    三、猴子和椰子

    可能你们也在其他地方看见过本题,设初始椰子为A,最后一次分给每个人的椰子为n,很容易推得一个A关于n得常数式子,因为要保证能够按照题目分下去,所以要保证每次分椰子都为正整数,已得答案为15621(5^6 - 4)。算是个小学数学题。

    输出15621即可。

    四、汉诺塔1

    这是一个汉诺塔的基本变式,其实用汉诺塔的思想去想也不难,要移动n根木根从A至C,可划分为下面几个步骤。

    • 先移动n-1根木根到C
    • 移动第n根木棍到B
    • 移动C上的n-1根木棍到A
    • 移动B上的第n根木棍到C
    • 移动A上的n-1根木棍到C

    至此,完成这个汉诺塔的移动,我们把移动n根木棍从A到C计为F(n)。

    则F(n) = F(n-1) + 1 + F(n-1) + 1 + F(n-1) = 3*F(n-1) + 2;且F(1) = 2;

    按照这个步骤写出递归函数即可。代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 long long fun(int n){
     5     if(n == 1)
     6         return 2;
     7     return 3*fun(n-1)+2;
     8 }
     9 
    10 int main(){
    11     freopen("test.in","r",stdin);
    12     freopen("test.out","w",stdout);
    13     int n;
    14     while(cin>>n){
    15         cout << fun(n) << endl;
    16     }
    17     return 0;
    18 }

    五、汉诺塔2

    在汉诺塔1的基础上多了可以把最长的木棍放在最上面,还是按照上面的步骤,移动n根木棍从A到C可划分为

    • 移动n-1根木棍从A到B
    • 移动第n根木棍到B上
    • 移动第n根木棍到C上
    • 移动n-1根木棍从B到C

    至此,完成汉诺塔的移动,我们把移动n根木根从A到C计为F(n),移动n根木棍到邻柱子计为T(n),关于T(n)的公式这里就不推了,演变方式一样。

    所以有F(n) = T(n-1)+1+1+T(n-1) = 2*T(n-1)+2;且F(1) = 2;

    代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 int fc(int n){
     5     if(n == 1)
     6         return 1;
     7     return 3*fc(n-1)+1;
     8 }
     9 
    10 int fun(int n){
    11     if(n == 1)
    12         return 2;
    13     return 2*fc(n-1)+2;
    14 }
    15 
    16 int main(){
    17     freopen("test.in","r",stdin);
    18     freopen("test.out","w",stdout);
    19     int n;
    20     while(cin>>n){
    21         cout << fun(n) << endl;
    22     }
    23     return 0;
    24 }

    六、整数划分

    其实这个题题面以及给出了一点提示,把n分成m个正整数的和,要直接求n的划分个数比较难,但是我们可以求n-1、n-2,,,的划分,设n = ΣDi,且max(Di) <= k,

    即Di为n的一种划分方案,最大数字不超过k,把它计为F(n,k),那么本题也就是求F(n,n);根据 n,k的不同大小关系,可得下列递归式

    • F(n, k) = 1; (n =1 or k = 1)
    • F(n, k) = F(n, n); (n < k)
    • F(n, k) = F(n, k-1) + 1; (n = k)
    • F(n, k) = F(n-k, k) + F(n, k-1); (n > k)

    所以可以写出代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 int fun(int n,int m){
     5     if(n == 1 || m == 1)
     6         return 1;
     7     else if(n < m)
     8         return fun(n,n);
     9     else if(n == m)
    10         return fun(n,n-1)+1;
    11     else if(n > m)
    12         return fun(n,m-1)+fun(n-m,m);
    13 }
    14 
    15 int main(){
    16     freopen("test.in","r",stdin);
    17     freopen("test.out","w",stdout);
    18     int n;
    19     while(cin>>n){
    20         cout << fun(n,n) << endl;
    21     }
    22     cerr << clock() << endl;
    23     return 0;
    24 }

    七、跳台阶

    其实很容易推出对于n阶台阶的方案有

    F(n) = Σ(F(n-Pi)); (P1 = 1,Pi = 质数集合)

    边界条件F(1) = F(0) = 1;

    所以代码显而易见了,其实本题还想卡一下数据范围,因为34好像就会爆int,还是算了,就只给你们弄到30,而且本题开了4s,其实我STD只跑了1.7s,奈何只能取整,干脆就4s得了。

    代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 int prime[1100],primesize,phi[1100];
     5 bool isprime[1100];
     6 
     7 int n;
     8 
     9 void getlist(int listsize){
    10     memset(isprime,1,sizeof(isprime));
    11     isprime[1]=false;
    12     for(int i=2;i<=listsize;i++)
    13     {
    14         if(isprime[i])prime[++primesize]=i;
    15          for(int j=1;j<=primesize&&i*prime[j]<=listsize;j++)
    16          {
    17             isprime[i*prime[j]]=false;
    18             if(i%prime[j]==0)break;
    19         }
    20     }
    21     prime[0] = 1;
    22 }
    23 
    24 int fun(int n){
    25     int sum = 0;
    26     if(n == 1 || n == 0)
    27         return 1;
    28     for(int i = 0; i <= primesize; i++){
    29         if(n < prime[i])
    30             break;
    31         sum += fun(n-prime[i]);
    32     }
    33     return sum;
    34 }
    35 
    36 int main(){
    37     freopen("test.in","r",stdin);
    38     freopen("test.out","w",stdout);
    39     ios_base::sync_with_stdio(false);
    40     getlist(100);
    41     int n;
    42     while(cin>>n){
    43         cout << fun(n) << endl;
    44     }
    45     cerr << clock() <<endl;
    46 }

    八、汉诺塔3

    四柱汉诺塔问题,我都懒得写如何推了,易得下面递归式

    F(n) = min(2*F(n-r)+2r-1),1 <= r <= n;

    但是直接用上面式子写递归式肯定会时间爆炸,不信你试试。

    最大n不超过100,所以其实你先递推出任意一项就可以了= =,这样的话预处理时间为O(n2),查询时间为O(1)。为了你们好= =所以我没有卡这种算法。

    但其实也可以得出一般式,根据Frame-Stewart算法可得出当r = floor((sqrt(8*n+1)-1)/2)时,有最小值,且最小值F(n) = (n - (r2-r+2)/2)*2r+1;

    即可以在O(1)时间内得出任意n根木棍需要的最小转移次数。

    代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 int a[105] = {0};
     5 
     6 int init(){
     7     fill(a,a+101,0x3f3f3f3f);
     8     a[0] = 0,a[1] = 1,a[2] = 3;
     9     for(int i = 3; i <= 100; i++){
    10         for(int j = 1; j < 32; j++){
    11             temp = 2*a[j]+pow(2LL,(long long)j)-1;
    12             a[i] = min(a[i],temp);
    13         }
    14     }
    15 }
    16 
    17 int main(){
    18     freopen("test.in","r",stdin);
    19     freopen("test.out","w",stdout);
    20     int n;
    21     while(cin>>n){
    22         int r = floor((sqrt(8*n+1)-1.0)/2.0);
    23         int ans = (n-(r*r-r+2)/2.0)*pow(2.0,r)+1;
    24         cout << ans << endl;
    25     }
    26     cerr << clock() << endl;
    27     return 0;
    28 }

    九、夏目友人帐(一)

    经典RMQ问题,ST/线段树都可以。

    本来想卡线段树做法,想了一下不卡算了= =。

    代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 //线段树
     5 #define lson l,m,p<<1
     6 #define rson m+1,r,p<<1|1
     7 #define Max(a,b) (a<b?b:a)
     8 #define Min(a,b) (a<b?a:b)
     9 #define INF 999999999
    10 
    11 int N,M;
    12 int MaxP[100000*4+10];
    13 int maxT;
    14 void Update(int val,int K,int l,int r,int p){
    15     int m=(l+r)>>1;
    16     if(l==r){
    17         MaxP[p]=val;
    18         return;
    19     }
    20     if(K<=m)
    21         Update(val,K,lson);
    22     else
    23         Update(val,K,rson);
    24     MaxP[p]=Max(MaxP[p<<1],MaxP[p<<1|1]);
    25 }
    26 void Query(int L,int R,int l,int r,int p){
    27     int m=(l+r)>>1;
    28     if(L<=l&&r<=R){
    29         maxT=Max(maxT,MaxP[p]);
    30         return;
    31     }
    32     if(L<=m)
    33         Query(L,R,lson);
    34     if(R>=m+1)
    35         Query(L,R,rson);
    36 }
    37 int main(){
    38     freopen("test2.in","r",stdin);
    39     freopen("test2.out","w",stdout);
    40     int i,val,a,b;
    41     scanf("%d %d",&N,&M);
    42     for(i=1;i<=N;i++){
    43         scanf("%d",&val);
    44         Update(val,i,1,N,1);
    45     }
    46     for(i=1;i<=M;i++){
    47         scanf("%d %d",&a,&b);
    48         maxT=0;
    49         Query(a,b,1,N,1);
    50         printf("%d
    ",maxT);
    51     }
    52     cerr << clock() << endl;
    53     return 0;
    54 }
    55 
    56 //ST算法
    57 int N,M;
    58 int A[500005];
    59 int FMax[500005][27];
    60 
    61 void Init(){
    62     int i,j;
    63     for(i=1;i<=N;i++)
    64         FMax[i][0]=A[i];
    65     for(i=1;(1<<i)<=N;i++){
    66         for(j=1;j+(1<<i)-1<=N;j++){
    67             FMax[j][i]=max(FMax[j][i-1],FMax[j+(1<<(i-1))][i-1]);
    68         }
    69     }
    70 }
    71 
    72 int Query(int l,int r){
    73     int k=(int)(log(r-l+1)/log(2));
    74     return max(FMax[l][k],FMax[r-(1<<k)+1][k]);
    75 }
    76 
    77 int main(){
    78     freopen("test1.in","r",stdin);
    79     freopen("test1.out","w",stdout);
    80     int i,a,b;
    81     scanf("%d %d",&N,&M);
    82     for(i=1;i<=N;i++)
    83         scanf("%d",&A[i]);
    84     Init();
    85     for(i=1;i<=M;i++){
    86         scanf("%d %d",&a,&b);
    87         printf("%d
    ",Query(a,b));
    88     }
    89     cerr << clock() << endl;
    90     return 0;
    91 }

    十、夏目友人帐(二)

    这里就不说怎么推的了,到时候认真听我黑板上讲23333。

    代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 
     5 const int N = 210;
     6 const int Q = 1010;
     7 int f[Q][N][N];
     8 int p[Q];
     9 int c[N][N];
    10 
    11 int main(){
    12     freopen("test3.in","r",stdin);
    13     freopen("test3.out","w",stdout);
    14     ios_base::sync_with_stdio(false);
    15     cin.tie(0);
    16     cout.tie(0);
    17     int n,q;cin>>n>>q;
    18     for(int i = 1; i <= n; i++){
    19         for(int j = 1; j <= n; j++){
    20             cin>>c[i][j];
    21         }
    22     }
    23     for(int i = 1; i <= q; i++){
    24         cin>>p[i];
    25     }
    26     memset(f,0x3f,sizeof(f));
    27     int INF = f[0][0][0];
    28     p[0] = 3;
    29     f[0][1][2] = 0;
    30     for(int i = 1; i <= q; i++){
    31         for(int x = 1; x <= n; x++){
    32             for(int y = 1; y <= n; y++){
    33                 if(x != p[i] && y != p[i])
    34                     f[i][x][y] = min(f[i][x][y],f[i-1][x][y]+c[p[i-1]][p[i]]);
    35                 if(p[i] != y && p[i] != p[i-1])
    36                     f[i][p[i-1]][y] = min(f[i][p[i-1]][y],f[i-1][x][y]+c[x][p[i]]);
    37                 if(p[i] != x && p[i] != p[i-1])
    38                     f[i][x][p[i-1]] = min(f[i][x][p[i-1]],f[i-1][x][y]+c[y][p[i]]);
    39             }
    40         }
    41     }
    42     int ans = INF;
    43     for(int i = 1; i<= n; i++){
    44         for(int j = 1; j<= n; j++){
    45             ans = min(ans,f[q][i][j]);
    46         }
    47     }
    48     cout << ans << endl;
    49     cerr << clock() << endl;
    50     return 0;
    51 }

    P.S. 其实很多题目都可以卡你们算法,不卡不卡,怕被打= =,后面给你们出卡常数233333

  • 相关阅读:
    wpf 计算公式
    c#读取文件
    wpf布局控件总结
    一个接口多次使用的值得引起思考的小片段
    wpf之渐变色LinearGradientBrush
    路由
    HtmlHelper2
    HtmlHelper1
    ssh连接Ubuntu之access denied
    百万级数据库优化方案
  • 原文地址:https://www.cnblogs.com/xenny/p/9971264.html
Copyright © 2020-2023  润新知