问题 H: 铺地砖
时间限制: 1.000 Sec 内存限制: 128 M
题目描述
一天,晨晨的数学老师布置了一道题目,大意如下:用1×1和2×2的磁砖不重叠地铺满n×3的地板,共有多少种方案?
例如:n=1时:1×3的地板方法就一个,直接由三个1×1的磁砖铺满。
n=2时:2×3的地板可以由下面3种方案铺满:
输入
第一行:一个整数n(1≤n≤100)。
输出
输出铺满n×3的地板的方案数。
样例输入
3
样例输出
5
提示
对于20%的数据,1≤n≤15;
对于50%的数据,1≤n≤30;
对于100%的数据,1≤n≤100;
题解:
这个题!!!
找到规律还没有结束!居然当n为100时会超出long long int的表示范围...
所以还要用上“高精度”? 用数组来储存超大数的每一位,并进行运算。
那...我们先来找规律
我想了好一会儿,基本上这种题都需要用到“递推”的思想,那我们来想想看,一个n行3列的地板,不就是在n-1行3列的地板上再加了1行嘛。
我们不妨设想额外加的这一行是由1*1的地砖铺满的,用M[n]表示n行3列地板有多少种铺法。
那么 M[n]=M[n-1]+“未知数x”
没有算出来“x” 是第n行和第n-1行中有2*2地砖的铺法。
我们把2*2地砖看作由4个1*1地砖组合而来
第n行和第n-1行如果要想有2*2的地砖,也即代表着第n-1行和第n行要有4个紧邻的1*1的地砖。
也就代表着.....
第n-1行也必须全是1*1地砖!
通过题目描述我们看到 2行3列有3种铺法,其中有2*2地砖的铺法是2种。
所以这个“x” 是 n-1行3列地板中第n-1行全为1*1地砖时的总铺法 再乘以2。
n-1行3列地板中第n-1行全为1*1地砖时的总铺法 ? 哇,我迷糊了, 而这个实际上可以转化为求 n-2行3列地板的总铺法呀, 因为第n-1行的铺法是固定了的。
答案在这里呼之欲出啦,我们求出来了!
M[n] = M[n-1] + M[n-2]*2
所以,我立马写了这样的一段代码:
#include <cstdio>
typedef long long int ll;
using namespace std;
ll dp[110];
int main(){
dp[0]=1;dp[1]=1;dp[2]=3;
ll n,i;
scanf("%lld",&n);
for(i=2;i<=n;i++)
dp[i]=dp[i-1]+dp[i-2]*2;
printf("%lld",dp[n]);
}
82/100 : (
想要过剩下的测试案例,还需要用上高精度。
所以...我们来看AC代码吧?
#include <cstdio>
typedef long long int ll;
using namespace std;
int dp[110];
void plu(int *a,int *b,int *c){ //用数组实现两个大数相加,结果储存在*c指向的数组
int jw=0;
while(*a!=-1&&*b!=-1){
*c=(*a+*b+jw)%10;
jw=(*a+*b+jw)/10;
a++;
b++;
c++;
}
while(*a!=-1){
*c=(*a+jw)%10;
jw=(*a+jw)/10;
c++;
a++;
}
while(*b!=-1){
*c=(*b+jw)%10;
jw=(*b+jw)/10;
c++;
b++;
}
while(jw){
*c=jw%10;
jw/=10;
c++;
}
*c=-1;
}
void mul(int *a, int b, int *c){ //实现大数乘以一个较小的数,结果储存在*c指向的数组
int jw=0;
while(*a!=-1){
*c=((*a)*b+jw)%10;
jw=((*a)*b+jw)/10;
a++;
c++;
}
while(jw){
*c=jw%10;
c++;
jw/=10;
}
*c=-1;
}
void prt(int *a){ //这是一个逆序输出数组的函数...非必要
int cnt=0;
for(;*a!=-1;a++)
cnt++;
a--;
while(cnt--){
printf("%d",*a);
a--;
}
printf("\n");
}
void mcpy(int *a,int *b){ //这个实现了“a=b”,将一个大数从*b指向的数组中copy到*a指向的数组
while(*b!=-1){
*a=*b;
a++;
b++;
}
*a=-1;
}
int main(){
int t0[110]={1,-1},t1[110]={1,-1},t2[110]={3,-1},t3[110];
ll n,i;
scanf("%lld",&n);
if(n>=3){
for(i=3;i<=n;i++){
mul(t1,2,t0); //用函数的话,个人感觉程序运行过程简洁直观,而且还容易调试BUG。
plu(t2,t0,t3); //如果把上面那些复制过来,可以想象这里会乱成什么样...
mcpy(t1,t2); //而且我也就不会有这么大的空间来加注释,说些废话了:)
mcpy(t2,t3);
}
prt(t3);
}
else
switch (n) {
case 1:
printf("1");
break;
case 2:
printf("3");
break;
}
}