HDOJ 2044-2050递推For Beginner

题目传送门: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 骨牌铺方格


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 折线分割平面

这个是真不会……


如果是直线,可以与前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……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值