Uva 12627 Erratic Expansion(递归)

这道题大体意思是利用一种递归规则生成不同的气球,问在某两行之间有多少个红气球。

我拿到这个题,一开始想的是递归求解,但在如何递归求解的思路上我的方法是错误的。在研读了例题上给出的提示后豁然开朗(顺便吐槽一下算法竞赛第二版在这这道题目上(P246)提示写的有问题,g(k,i)=2g(k-1,i-2^(k-1))+c(k-1)  ,他把c(k-1)写成了c(k)...我纠结这个纠结了好久)

根据题目提示,这道题可以用f(k,i)表示k小时后最上边i行的红气球总数

那么我们的答案就可以表示为f(k,b)-f(k,a-1)

如何求f(k,i)?

这里我们通过观察,在k小时后所分成的四部分气球中,最右下角区域的气球是跟其他区域气球不相同的。因此我们可以分成两种情况(i在上半部分和i在下半部分)。

当i<2^(k-1),f(k,i)=2*f(k-1,i)

当i>=2^(k-1),f(k,i)=c(k-1)+f(k-1,i-2^(k-1))

其中c(k-1)代表完整的那部分的红气球数,也就是k-1小时后的红气球数。

由气球构造方式可得递推式:c(k)=3c(k-1),c(0)=1,可知这是个等比数列,求得c(k)=3^k

至此我们就可以得到我们想要的答案了。

同理,我们也可以利用g(k,i)表示k小时后最下边i行的红气球总数,把我们的答案用g(k,i)表示出来,这里不再叙述。

以下是完整代码:

 1 #include <cstdio>
 2 using namespace std;
 3 long long c(int k){
 4     long long sum = 1;
 5     while(k--)sum*=3;
 6     return sum;
 7 }
 8 long long g(int k, int i) {
 9   if(i == 0) return 0;
10   if(k == 0) return 1;
11 
12   int k2 = 1 << (k-1);
13   if(i >= k2) return 2*g(k-1, i-k2) + c(k-1);
14   else return g(k-1,i);
15 }
16 long long f(int k,int i){
17     if(i==0)return 0;//注意这两处边界的处理
18     if(k==0)return 1;
19     int t = 1 << (k-1);
20     if(i<t)return 2*f(k-1,i); 
21     else return  2*c(k-1) + f(k-1,i-t);
22 }
23 int main(){
24   int T, k, a, b;
25   scanf("%d",&T);
26   for(int kase = 1; kase <= T; kase++) {
27     scanf("%d%d%d",&k,&a,&b);
28     printf("Case %d: %lld
",kase,f(k, b) - f(k, a-1));
29     //int t =1 << k;
30     //printf("Case %d: %lld
",kase,g(k, t-a+1) - g(k,t- b));
31   }
32   return 0;
33 }
1 long long c(int i) { return i == 0 ? 1 : c(i-1)*3; }

另外代码仓库中c的求法比较简洁,值得学习。

原文地址:https://www.cnblogs.com/Wade-/p/6388592.html