关于未名湖边的烦恼问题
昨天,我很受伤,自尊心严重受损,一切的一切都来源于一道题——未名湖边的烦恼,这是蓝桥杯官网的一道题,我看到后一直没有思路,最后无奈之下,我去百度上找了一篇讲这个题的博客,轻轻松松的就找到了,然而它里面的一句话刺痛了我:在蓝桥杯官网看到这道题,很水的一道题……这还不是最打击我的,当我看到他的代码,只用了区区几行代码,虽然有些小问题,但是把核心问题解决了,因为代码太简洁,我甚至都没有看懂其中的个中缘由……感觉自己是弱鸡,弱爆了。我花了好大会儿菜看明白这个方法的思路。
不说了,说多了都是眼泪,让我们来具体分析这道题吧。题如下:
问题描述
每年冬天,北大未名湖上都是滑冰的好地方。北大体育组准备了许多冰鞋,可是人太多了,每天下午收工后,常常一双冰鞋都不剩。
每天早上,租鞋窗口都会排起长龙,假设有还鞋的m个,有需要租鞋的n个。现在的问题是,这些人有多少种排法,可以避免出现体育组没有冰鞋可租的尴尬场面。(两个同样需求的人(比如都是租鞋或都是还鞋)交换位置是同一种排法)
输入格式
两个整数,表示m和n
输出格式
一个整数,表示队伍的排法的方案数。
样例输入
3 2
样例输出
5
数据规模和约定
m,n∈[0,18]
最初看到这个题,我在思考,如何去用递推解决,像二岔数列那样的解决方案,然而我一直想不开,也在纠结如何去除题上所说的重复的问题。当我看到代码时,我懵了,竟然用的是递归,我一直以为需要用递推才行,看来一开始思路就错了,那么剩下的都是无用功,南辕北辙,所以做题一定要先给自己定好方向,如果真的想不开,那就换个角度想想,正着不行,咱就试试反着推,递推不行,就想想递归……诸此等等,都是方法。然而道理是这么讲的,但是他的递归我竟然没看懂。首先,为了方便解说,代码如下:
#include <stdio.h>
int FangAn(int m,int n);
int main(int argc, const char * argv[])
{
int m=0,n=0,s=0;
scanf("%d%d",&m,&n);
s=FangAn(m, n);
printf("%d\n",s);
return 0;
}
int FangAn(int m,int n)
{
if (m<n)
{
return 0;
}
if (n==0)
{
return 1;
}
return FangAn(m-1, n)+FangAn(m, n-1); // 起初,我并不能理解这部分
}
这个递归感觉代码好简单,只是我根本不了解其本质,经过我的几轮研究,我才初步了解。
当我遇见这种抽象的代码时,我习惯先实例化一个简单的数据去尝试解题。它的递归的部分真的很好,主题思想就是一点点把大问题缩小成小问题。最初我不懂,这部分’return FangAn(m-1, n)+FangAn(m, n-1);’代码究竟是何种思想,当我把样例的数据代入演算时,我懂了一丢丢。
例如:
FangAn(3,2)=FangAn(2,2)+FangAn(3,1)
=FangAn(1,2)+FangAn(2,1) + FangAn(2,1)+FangAn(3,0)
=0 + FangAn(1,1)+FangAn(2,0) + FangAn(1,1)+FangAn(2,0) + 1
=0 + FangAn(0,1)+FangAn(1,0) + 1 + FangAn(0,1)+FangAn(1,0) + 1 + 1
=0+0+1+1+0+1+1+1
=5
就这个破题,我初步想通了,就是一次固定一个位置,然后去固定下一个位置,以此类推,一直到所有的都固定好,即得到结果。
但是随之问题来了,第一个位置只能固定为一个还鞋子的人,那么这个递归就不能正着实现了,对了,不能正着实现,那么我们就反着实现,毕竟最后一个位置可以是还鞋子的,也可以是借鞋子的,那么这个题的思路就是从最后一个位置开始固定,逐次固定到第一个,当 m < n 时则返回0,当 n == 0 时则返回1,就这样递归,最终得到的是所有的可行的方案数。
就这样,这道题算是结束了,告了一段落……不容忽视的问题摆在我面前,想要快点进步,那我就必须多学习,只靠自己的聪明是无法解决所有问题的,需要大量的补充知识空洞,来让自己能更加灵活的运用自如……-_-#