不容易系列之(3)—— LELE的RPG难题
1个格子的时候 3种
2个格子的时候 6种
3个格子的时候 6种
4个格子的时候:
分两种情况:
那么怎么计算有4个格子的情况呢?
设函数f(n), f(1) = 3; f(2) = 6; f(3) = 6;
f(4) = f(3) + 2*f(2)=18;
当有n个格子,也分为两种情况,我们只需要考虑最后2个格子的情况
从而有递推公式:f(n) = f(n-1) + 2*f(n-2)
#include <stdio.h>
#define MAX 55
_int64 f_array(int);
int main()
{
int n;
__int64 res;
while(scanf("%d", &n) != EOF)
{
if(n <= 0 || n > 50)
{
printf("Input error!Please try again!\n");
break;
}
res = f_array(n);
printf("%I64d\n", res);
}
return 0;
}
__int64 f_array(int n)
{
__int64 a[MAX] = {
0,3,6,6
};
int i;
for(i = 4; i <= n; i++)
{
a[i] = 2*a[i-2] + a[i-1];
}
return a[n];
}
骨牌铺方格
n = 1,一种方案
n = 2,两种方案
n = 3,三种方案
n = 4,五种方案
..........
n = N 呢?
如果对数字比较敏感,那么可以看出这会是一个斐波那契问题。
当有n格时,分两中情况:
从而,总的方法f(n) = f(n-1) + f(n - 2)
#include <stdio.h>
#define MAX 55
__int64 f(int);
int main()
{
int n;
__int64 res;
while(scanf("%d", &n) != EOF)
{
if(n <= 0 || n > 50)
{
printf("Input error!\n");
break;
}
res = f(n);
printf("%I64d\n", res);
}
return 0;
}
__int64 f(int n)
{
int i;
__int64 a[MAX] = {
0, 1, 2, 3
};
for (i = 4; i <= n; i++)
{
a[i] = a[i-1] + a[i-2];
}
return a[n];
}
阿牛的EOF牛肉串
本题不同于RPG难题,RPG难题是从前往后推,我们先确定第n-1个方格的涂色情况,然后在确定第n格可以涂哪几种颜色。
本题先考虑第n格可以填哪几个字母,再往前推。
两种情况:
所有,有
f(n) = 2 * [f(n-1) + f(n-2)]
#include <stdio.h>
int main()
{
int i, n;
__int64 temp, pre_res, res;
while(scanf ("%d", &n) != EOF)
{
if(n <= 0 || n >= 40)
break;
pre_res = 3;
res = 8;
if(n == 1)
printf("%I64d\n", pre_res);
else if(n == 2)
printf("%I64d\n", res);
else
{
for(i = 3; i <= n; i++)
{
temp = res;
res = 2*(pre_res + res);
pre_res = temp;
}
printf("%I64d\n", res);
}
}
return 0;
}
2048
本题考察错排。所以,不知道错排的概念就不知道如何下手。
错排定义:维基百科定义
我们采用递推的方法研究错排公式:
当n个编号元素放在n个编号位置,用f(n)表示元素编号与位置编号不对应的错排数.
显然f(1)=0,f(2)=1。当n≥3时,不妨设n排在了第k位,其中k≠n,也就是1≤k≤n-1。那么我们现在考虑第n位的情况。
当k排在第n位时,除了n和k以外还有n-2个数,其错排数为f(n-2)。
当k不排在第n位时,那么将第n位重新考虑成一个新的“第k位”,这时的包括k在内的剩下n-1个数的每一种错排,都等价于只有n-1个数时的错排(只是其中的第k位会换成第n位)。其错排数为f(n-1)。
综上得到:
f(n)=(n-1)*[f(n-1)+f(n-2)],其中:f(1)=0,f(2)=1
有了这个递推公式就好办了,我们先求出错排的总数,然后再求出所有情况总数(就是n的阶乘)。最后,输出两位的百分比数。
#include <stdio.h> int main() { int c, n, i; long long pre_res, res, temp, denom; /* 输入行数 */ while(scanf("%d", &c) != EOF) { while(c--) { scanf("%d", &n); if(n <= 1 || n > 20) return 0; pre_res = 0; res = 1; if (n > 2) { /* 求错排总数 */ for(i = 3; i <= n; i++) { temp = res; res = (i-1) * (pre_res + res); pre_res = temp; } } /* 初始化分母为1 */ denom = 1; /* 计算n的阶乘 */ for(i = 1; i <= n; i++) { denom *=i; } printf("%.2lf%%\n", (double)res/denom*100); } } return 0; }
2049
理解了2048题之后,再来看本题,你会发现这一题的本质还是个错排问题。
区别在于
#include <stdio.h>
int main()
{
int c, m, n, i;
long long pre_res, res, temp;//求错排
long long fac, denom, numer; //求组合数
while(scanf("%d", &c) != EOF)
{
while(c--)
{
scanf("%d%d", &n, &m);
if (m > n || m > 20 || n <= 1)
break;
/* m个新郎找错新娘的情况种类 */
pre_res = 0;
res = 1;
for(i = 3; i <= m; i++)
{
temp = res;
res = (i - 1)*(pre_res + res);
pre_res = temp;
}
/* 从n对新婚夫妇中,选取m个犯错的新郎 */
denom = numer = 1;
for(i = 0; i < m; i++)
{
numer *= (n-i);
denom *= (m-i);
}
fac = numer/denom;
printf("%lld\n", fac * res);
}
}
}
2050
题目大致如:n条直线,最多可以把平面分为多少个区域
分析:当有n-1条直线时,平面最多被分成了f(n-1)个区域。则第n条直线要是切成的区域数最多,就必须与每条直线相交且不能有同一交点。 这样就会得到n-1个交点。这些交点将第n条直线分为2条射线和n-2条线段。而每条射线和线段将以有的区域一分为二。这样就多出了2+(n-2)个区域。
故:f(n)=f(n-1)+n =
根据直线分平面可知,由交点决定了射线和线段的条数,进而决定了新增的区域数。当n-1条折线时,区域数为f(n-1)。为了使增加的区域最多,则折线的两边的线段要和n-1条折线的边,即2*(n-1)条线段相交。那么新增的线段数为4*(n-1),射线数为2。但要注意的是,折线本身相邻的两线段只能增加一个区域。
故:f(n)=f(n-1)+2*[2*(n-1)+1] -1
#include <stdio.h> int main() { int c, n, i; long long pre_res, res; while (scanf("%d", &c) != EOF) { while (c--) { scanf("%d", &n); pre_res = 2; if (n < 1 || n > 10000) return ; if (n == 1) printf("%lld\n", pre_res); else { for (i = 2; i <= n; i++) { res = pre_res + 2*(2*(i-1) + 1) - 1; pre_res = res; } printf("%lld\n", pre_res); } } } return 0; }