题目传送门:http://acm.hdu.edu.cn/search.php?field=problem&key=%B5%DD%CD%C6%C7%F3%BD%E2%D7%A8%CC%E2%C1%B7%CF%B0%A3%A8For+Beginner%A3%A9&source=1&searchmode=source
一边百度一边做完了这7道题,头昏脑胀,趁着现在还会做,赶紧mark一下,不然明天再看代码就是外星人的了……
2044 一只小蜜蜂
蜜蜂只能往右爬还不能回头,给你起始点和终点,求可选路径一共有多少。
分析一下可以知道,这玩意其实和起点和终点的绝对位置没关系,只跟相对位置(它们的差)有关系。
设f(n)是距离为n时可选路径的数目。
先列一列f(1)=1,f(2)=2,f(3)=3,f(4)=5
等等,有没有一丝熟悉的味道?
由题目可知,到达 一个蜂房只能从它的上面或者左面走过来,例如,想要到达6,只能从4或者5走过来,也就是说
f(n)=f(n-1)+f(n-2)
没错,就是斐波那契数列。
#include <stdio.h>
int main()
{
int n,i;
long long f[55]={0,1,2};
for (i=3;i<50;i++) {
f[i] = f[i-1] +f[i-2];
}
scanf("%d",&n);
while (n--) {
int a,b;
scanf("%d%d",&a,&b);
printf("%lld\n",f[b-a]);
}
return 0;
}
注意数据范围, 斐波那契数列到第50位还是不小的。
2045 不容易系列之(3)—— LELE的RPG难题
RPG,三个字母任选排成一个长度为n的串,相邻元素不能相同且首位不能相同,问排列共有多少种。
还是先列一列再说。
f(1)=3 f(2)=6 f(3)=6
似乎看不出什么?好吧,找找规律。
假设s是长度为n-1的一个合法的串,如果要加一个字符使其依然是合法的串,那么只能有一种选择。
难道f(n)=f(n-1)?这显然不正确。
那么如果s是长度为n-2的一个合法的串,是不是可以先添加一个和首位相同的字符,再在最后一位添加一个字符使串合法?Of Course ,那么此时在串的最后一位就有两种填法。如果在s的倒数第二位添加的是不与首位相同的字符,那么问题就转换成了在长度为n-1的串最后添加一位使串合法,和上面讨论的相同了。
递推公式
f(n) = f(n-1)+2*f(n-2)
#include <stdio.h>
int main()
{
double f[55] = {0,3,6,6};
int i;
for (i=4;i<=50;i++) {
f[i] = f[i-1] + 2*f[i-2];
}
int n;
while (~scanf("%d",&n)) {
printf("%.0f\n",f[n]);
}
return 0;
}
似乎当时用long long还是超了范围?于是用起了double
2046 骨牌铺方格
![](https://i-blog.csdnimg.cn/blog_migrate/396f212ed2d31ef1fd2e921eb1b248b2.jpeg)
2*n的方格,让你用1*2的长方体填充,问有多少种情况?
列一下
f(1)=1 f(2)=2 f(3)=3
咦,难道又是斐波那契?
不妨照着之前的思路想一想,如果n-1的情况下已经铺好了,那么要铺好一个2*n的方格,只能在多出的2*1中竖着放置一个长方形,所以只有一种情况。
而如果在n-2的情况下已经铺好,铺余下2*2的方格有两种方法:两个长方形都竖放或者都横放,而如果竖放,问题就转化成了上面n-1已经铺好的情况,所以在n-2情形下也只有一种情况。
递推公式 f(n)=f(n-1)+f(n-2)
#include <stdio.h>
int main()
{
int n;
double f[55]={0,1,2,3,};
int i;
for (i=4;i<51;i++) {
f[i] = f[i-1] + f[i-2];
}
while (~scanf("%d",&n)) {
printf("%.0f\n",f[n]);
}
return 0;
}
2047 阿牛的EOF串
EOF三个字符组成长度为n的串,其中不允许存在OO字串,问一共有多少种可能。
f(1)= 3 f(2)=8 f(3)=22
分析一下,如果没有不允许OO存在的限制条件,显然答案就是3的n次方,从递推的角度看,f(n)=f(n-1),加上了限制条件,显然f(n)会减少,少了哪些呢?n-1是末尾的字符是O的情况下再一次在末尾填O的情况。
因此,需要求的合法串长度为n时末尾为O的串的个数g(n)
那么就可得 f(n)=3*f(n-1)-g(n-1)
如何求得g(n)呢
不妨先列一下
g(1)=1 g(2)=2 g(3)=6
可以发现g(n)=f(n-1)-g(n-1)
这也很容易理解,每次在填充字符,无非就是填充EOF,故原来的每个合法串填充O都会变成末尾为O的串,但这些串不一定都是合法的,只有在没有填充O之前的末尾不是O的情况下填充O才会合法。而这样的串的个数就是f(n-1)-g(n-1)
#include <stdio.h>
int main()
{
int n,i;
long long f[45]={0,3,8,22,},g[45]={0,1,2,6,};
for (i=4;i<40;i++) {
f[i] = 3*f[i-1] - g[i-1];
g[i] = f[i-1] - g[i-1];
}
while (~scanf("%d",&n)) {
printf("%lld\n",f[n]);
}
return 0;
}
2048 神、上帝以及老天爷
可以理解为n个人对应拥有n个物品,而n个人全部没有选到自己的物品
如果一开始n-1个人满足题意,这时候来了第n个人拿着自己的物品,要想让他不拿着自己的物品,他可以和剩余的n-1个人中的任意一个交换物品,达到目的。
OK 递推公式就是 f(n)=(n-1)f(n-1)啦!
WAWAWA
……
考虑n-1个人时,这时候有一个人是拿着自己的物品的,这时候又来了一个人,要想达到“目的”,他们两人交换物品,此时也符合题意。
所以还有一种情况就是(n-1)f(n-2)
递推公式 f(n)=(n-1)(f(n-1)+f(n-2))
这类问题还有一个名字:错排问题
#include <stdio.h>
long long multi(int m)
{
long long ret = 1;
int i;
for (i=2;i<=m;i++)
ret *= i;
return ret;
}
int main()
{
int n;
scanf("%d",&n);
long long f[25]={0,0,1,};
int i;
for (i=3;i<=20;i++) {
f[i] = (i-1)*(f[i-1]+f[i-2]);
}
while (n--) {
int m;
scanf("%d",&m);
double ans = 100.0*f[m]/multi(m);
printf("%.2f%%\n",ans);
}
return 0;
}
2049 不容易系列之(4)——考新郎
n个人里面有m个人的物品(新娘……)是拿错了的,求一共多少情况。
有了上面一题的铺垫,尤其是发现了错排问题这个概念之后,这道题就没有多么难了,错排+组合
m个人拿错(新娘……),那么就是求出f(m),有递推公式f(m)=(m-1)(f(m-1)+f(m-2))
一共有n个人?从n个人里面挑m个出来让他们拿错(新娘……)就好了。
#include <stdio.h>
long long multi(int m)
{
long long ret = 1;
int i;
for (i=2;i<=m;i++)
ret *= i;
return ret;
}
long long C(int n,int m)
{
return multi(n)/multi(m)/multi(n-m);
}
int main()
{
int c;
scanf("%d",&c);
long long f[25]={0,0,1,};
int i;
for (i=3;i<=20;i++) {
f[i] = (i-1)*(f[i-1]+f[i-2]);
}
while (c--) {
int n,m;
scanf("%d%d",&n,&m);
printf("%lld\n",C(n,m)*f[m]);
}
return 0;
}
2050 折线分割平面
![](https://i-blog.csdnimg.cn/blog_migrate/f38a5cb2aa4e9b9d447d934a7c1b24e7.jpeg)
这个是真不会……
如果是直线,可以与前n-1条线有n-1个交点,比原来多出n-1+1个区域
改成折线,那么其实可以想象成每次添加两条线,第一条线与之前2(n-1)条线至多有2(n-1)个交点,可以增加2(n-1)+1个区域,而第二条线与之前的2(n-1)+1条直线有交点,可以多出2(n-1)+1+1个区域,但由于是折线,这两条线并不能反向延长,故比正常添加两条直线少了两个区域。
递推公式 f(n)=f(n-1)+2(n-1)+1+2(n-1)+1+1-2 = f(n-1)+4n-3
#include <stdio.h>
int main()
{
long long f[10001]={0,2,7,};
int i;
int n;
scanf("%d",&n);
for (i = 3;i<=10000;i++) {
f[i] = f[i-1] + 4*i-3;
}
while (n-- ) {
int m;
scanf("%d",&m);
printf("%lld\n",f[m]);
}
return 0;
}
beginner……