一道关于足球与棒子的题。总之简单来说就是n(0<=n<=45)个0、1中有几种排法使得排列中不含有连续的1。
这道题的解法有很多。
第一种:也是我先想到的一种。用高中的插空法来做,这样就很简单了。不过直接阶乘会爆long long,于是就失败了。如果用大数运算来做是可以做的,不过写起来太麻烦,于是就此放弃。
第二种:第一种方法虽然失败了,不过由其得到启发。第一种方法WA了,于是进行了打表检查,发现结果成斐波那契数列。这样就更好做了,直接用int做也没事。之后上网查了一下,找到了证明过程,下面会贴出来。
第三种:由第二种方法可以看出,只是一道简单的dp问题。那就直接dp吧。状态转移方程为a[i]=a[i-1]+a[i-2],a[0]=0,a[1]=2,a[2]=3。代码和第二种方法的代码一样(无意中直接用了状态转移方程)。
证明:
输入1时,结果有两种:0,1;输入2时,结果有00,01,10,输入三时,结果有000,001,010,100,101……无论输入几,总结过数都是最后一位为0的数字个数加上最后一位为1的数字个数,而当前一位要产生1,前一位最后只能是0,当前一位要产生0,前一位最后可以为1,也可以为0。
设O[i]、Z[i]分别表示i位数的最后一位为0的数和最后一位为1的数,f[i]表示i位数的总数,则f[i] = O[i] + Z[i]。又由以上分析,O[k] = Z[k-1] = O[k-2] + Z[k-2] = f[k-2], Z[k] = O[k-1] + Z[k-1] =f[k-1], So f[k] = O[k] + Z[k] = f[k-1] + f[k-2]。这不就是斐波拉契数列吗?f[1] = 2, f[2] = 3, f[k] = f[k-1] + f[k-2]。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
long long fi[50];
void calcu(){
fi[0]=0;
fi[1]=2;
fi[2]=3;
for(int i=3;i<=45;i++){
fi[i]=fi[i-1]+fi[i-2];
}
}
int main(){
int T,i;
cin>>T;
calcu();
for(i=1;i<=T;i++){
int n;
cin>>n;
printf("Scenario #%d:\n",i);
cout<<fi[n]<<endl<<endl;
}
return 0;
}
AC图:
附带:
附带代码(失败):
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
long long conbination(int m,int n){
long long up=1,down=1;
for(;m>0;m--,n--){
up*=n;down*=m;
}
cout<<up<<','<<down<<endl;
return (up/down);
}
int main(){
int T,i;
cin>>T;
for(i=1;i<=T;i++){
int n;cin>>n;
printf("Scenario #%d:\n",i);
if(n==0) cout<<'0'<<endl<<endl;
int m=1,middle=n/2;
long long number=1;
for(;n>=middle;n--,m++){
number+=conbination(m,n);
}
cout<<number<<endl<<endl;
}
return 0;
}