很高兴给大家出题,本次难度低于上一场,新生的六个题都可以直接裸递归式或者裸递推式解决,对于老生的汉诺塔3,需要找出一般式,后两题分别为裸ST算法(或线段树)/线性DP。
正确的难度顺序为
- 种花
- 角谷定律
- 猴子和椰子
- 汉诺塔1
- 汉诺塔2
- 整数划分
- 跳台阶
- 汉诺塔3
- 夏目友人帐(一)
- 夏目友人帐(二)
一、种花
本题很容易能推出递推式或者一般式,对于第一快地,有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