2045不容易系列之(3)—— LELE的RPG难题
-
分析
1个格子的时候 3种
2个格子的时候 6种
3个格子的时候 6种
4个格子的时候:
分两种情况:
- 前3个格子符合所有规则,即间隔颜色不同,同时第1个和第3个颜色也不同。这时候第4个格子只有1种选择
- 前3个格子不符合所有规则,即间隔颜色不同,但是第1个和第3个颜色相同。这时候,第4个格子有2种选择
那么怎么计算有4个格子的情况呢?
设函数f(n), f(1) = 3; f(2) = 6; f(3) = 6;
f(4) = f(3) + 2*f(2)=18;
当有n个格子,也分为两种情况,我们只需要考虑最后2个格子的情况
- 前n-1个格子符合所有规则,即间隔颜色不同,同时第1个和第n-1个颜色也不同。这时候第n个格子只有1种选择。前边n-1个格子有f(n-1)中涂法
- 前n-1个格子不符合所有规则,即间隔颜色不同,但是第1个和第n-1个颜色相同。这时候,第n个格子有2种选择。那么前n-2个格子有f(n-2)种可能,这种情况下有2*f(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];
}
2046 骨牌铺方格
-
题目
-
分析
n = 1,一种方案
n = 2,两种方案
n = 3,三种方案
n = 4,五种方案
..........
n = N 呢?
如果对数字比较敏感,那么可以看出这会是一个斐波那契问题。
当有n格时,分两中情况:
- 如果打算第n格竖着放,那么前边n - 1格可以任意摆放,共有f(n-1)种方法;
- 如果打算第n格横着放,那么要求第n - 1格也横着放。前n-2格可以任意摆放,共有f(n-2)种方法;
从而,总的方法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];
}
2047 阿牛的EOF牛肉串
题目
分析
本题不同于RPG难题,RPG难题是从前往后推,我们先确定第n-1个方格的涂色情况,然后在确定第n格可以涂哪几种颜色。
本题先考虑第n格可以填哪几个字母,再往前推。
两种情况:
- 第n格填‘E’或’F’,那么第n-1格可以填任意字符串,第n-2格有f(n-1)种方法
- 第n格填‘o’,那么第n-1格一定填'E'或'F',此时第n-2格有f(n-2)种方法
所有,有
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 不容易系列之(4)——考新郎
题目
分析
理解了2048题之后,再来看本题,你会发现这一题的本质还是个错排问题。
区别在于
- 需要先从n对新婚夫妇中选取m个新郎来进行错排
- 不需要求概率,求出多少中可能的情况即可
代码
#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条直线,最多可以把平面分为多少个区域
分析:当有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;
}