Fork me on GitHub

第二场周赛(递归递推个人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\n",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\n",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

posted @ 2018-11-16 20:55  Xenny  阅读(234)  评论(0编辑  收藏