这道题大体意思是利用一种递归规则生成不同的气球,问在某两行之间有多少个红气球。
我拿到这个题,一开始想的是递归求解,但在如何递归求解的思路上我的方法是错误的。在研读了例题上给出的提示后豁然开朗(顺便吐槽一下算法竞赛第二版在这这道题目上(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的求法比较简洁,值得学习。