递推(for beginner)
斐波那契兔子问题、斐波那契数-从爬楼梯问题、HDU2046 骨牌铺方格、HDU2044 一只小蜜蜂、HDU2048 神、上帝以及老天爷、HDU2049 不容易系列之(4)——考新郎、递推中的平面问题、HDU 2097 Children’s Queue、HDU2045 不容易系列之(3)—— LELE的RPG难题、HDU2047 阿牛的EOF牛肉、for beginner,HDU2563统计问题
递推的基本思想
- 首先确认,是否能很容易的得到简单情况的解
- 假设规模为N-1的情况已经得到解决
- 重点分析:当规模扩大到N时,如何枚举出所有的情况,并且要确保对于每一种子情况都能用已经得到的数据解决
- 编程中的空间换时间的思想
- 并不一定是从N-1到N的分析
递推中的经典——Fibonacci sequence(斐波那契数列)
先上递推中的fib数列的解题模板
1 |
|
应用:
最经典当然是兔子繁殖问题 斐波那契兔子问题
还有一个经典的上楼梯问题斐波那契数-从爬楼梯问题
-
自己代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using namespace std;
typedef long long ll;
int main()
{
int num;
while( cin >>num)
{
ll arr[100000] = {0};
arr[1] = 1;
arr[2] = 2;
int i;
for(i = 3; i <= num; i++)
arr[i] = arr[i - 1] + arr[i - 2];
cout << arr[num] << endl;
}
return 0;
}
主要还是推出这个状态可以由上次的状态推出。
小变形:HDU2044 一只小蜜蜂
解题:一只小蜜蜂
自己代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using namespace std;
int main()
{
int n;
cin >> n;
while(n--)
{
long long arr[10000];
arr[1] = arr[2] = 1;
int a , b;
cin >> a >> b;
int temp;
if(a>b) //保证总是从数字较小的蜂房到数字数字较大的蜂房,直接相减可能会出现负数
{
temp=a;
a=b;
b=temp;
}
b = b - a + 1; //化为斐波那契数列模式,即从3走到4可以化为求fib里面的f(2)
int i;
for(i = 3 ;i <= b; i++)
arr[i] = arr[i - 1] + arr[i - 2];
cout << arr[b];
// if(n != 0)
cout << endl;
}
return 0;
}
错排公式
-
解题:HDU2048
公式:M(n)=(n-1)[M(n-2)+M(n-1)],可以考虑参考上面理解
自己代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using namespace std;
typedef long long ll;
int main()
{
int n;
cin >> n;
while(n--)
{
int num;
ll arr[100000] = {0};
arr[1] = 0;
arr[2] = 1;
cin >> num;
int i;
for(i = 3; i <= num; i++) //所有人抽不到的结果
arr[i] = (arr[i - 1] + arr[i - 2]) * (i - 1);
ll all = 1;
for(i = 1; i <= num; i++) //所有可能出现的结果
all *= i;
double ans = arr[num] / (double)all;
printf("%.02lf%%\n", ans * 100); //四舍五入
}
return 0;
}-
解题:HDU2049
其实就是部分排错,利用组合公式实现成小部分群体中全部排错的效果,从而可以用公式
自己代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using namespace std;
typedef long long ll;
ll fun(int n) //求n!
{
ll num = 1;
for(int i = 1; i <= n; i++)
num *= i;
return num;
}
int main()
{
int t;
cin >> t;
while(t--)
{
int n, m;
cin >> n >> m;
ll arr[10000] = {0};
arr[1] = 0;
arr[2] = 1;
for(int i = 3; i <= m; i++) //公式
arr[i] = (i - 1) * (arr[i - 1] + arr[i -2]);
cout << fun(n) / fun(m) / fun(n - m) * arr[m] << endl;
//CMN=N!/M!/(N-M)!,然后再乘上公式得到的结果
}
return 0;
}
-
递推中的平面问题
n条直线分平面问题
封闭曲线分平面问题
折线分割平面
代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using namespace std;
typedef long long ll;
int main()
{
int n;
cin >> n;
while(n--)
{
int num;
cin >>num;
ll arr[100000] = {0};
arr[1] = 2;
int i;
for(i = 2; i <= num; i++)
arr[i] = arr[i - 1] + 4*(i - 1) + 1;
cout << arr[num] << endl;
}
return 0;
}- 平面割空间
- 圆圈分割平面
- 设有n条封闭曲线画在平面上,而任何两条封闭曲线恰好相交于两点,且任何三条封闭曲线不相交于同一点,问这些封闭曲线把平面分割成的区域个数。
可以看pdf,github.pdf)
假装前面排错系列
-
解析:
按照最后一个人的性别分析,他要么是男,要么是女,所以可以分两大类讨论:
- 如果n个人的合法队列的最后一个人是男,则对前面n-1个人的队列没有任何限制,他只要站在最后即可,所以,这种情况一共有F(n-1);
如果n个人的合法队列的最后一个人是女,则要求队列的第n-1个人务必也是女生,这就是说,限定了最后两个人必须都是女生,这又可以分两种情况:
- 如果队列的前n-2个人是合法的队列,则显然后面再加两个女生,也一定是合法的,这种情况有F(n-2);
- 但是,难点在于,即使前面n-2个人不是合法的队列,加上两个女生也有可能是合法的,当然,这种长度为n-2的不合法队列,不合法的地方必须是尾巴,就是说,这里说的长度是n-2的不合法串的形式必须是“F(n-4)+男+女”,这种情况一共有F(n-4).
所以,通过以上的分析,可以得到递推的通项公式:F(n)=F(n-1)+F(n-2)+F(n-4) (n>3)。然后就是对n<=3 的一些特殊情况的处理了,显然:F(0)=1 (没有人也是合法的,这个可以特殊处理,就像0的阶乘定义为1一样) F(1)=1,F(2)=2,F(3)=4
根据这个公式计算f(n),其数列值的增长速度太快了,需要构筑一个大整数类来解决。此处选择用java解决
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27import java.io.*;
import java.math.*;
import java.util.*;
public class Main
{
public static void main(String[] args)
{
int num;
Scanner in = new Scanner(System.in);
while(in.hasNextInt())
{
num = in.nextInt();
BigInteger[] arr = new BigInteger[10000];
arr[0] =BigInteger.valueOf(1);
arr[1] = BigInteger.valueOf(1);
arr[2] = BigInteger.valueOf(2);
arr[3] =BigInteger.valueOf(4);
for(int i = 4; i <= num; i++)
{
arr[i] = arr[i - 2].add(arr[i - 1]);
arr[i] = arr[i].add(arr[i - 4]);
}
System.out.println(arr[num]);
}
}
}
HDU2045 不容易系列之(3)—— LELE的RPG难题
解题:HDU2045
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using namespace std;
typedef long long ll;
int main()
{
int n;
while(cin >> n)
{
ll arr[100000] = {0, 3, 6, 6};
for(int i = 4; i <= n; i++)
arr[i] = 2 * arr[i - 2] + arr[i - 1];
cout << arr[n] << endl;
}
return 0;
}
其他
解题:HDU2047
1 |
|
for beginner:
- HDU1290 献给杭电五十周年校庆的礼物
- HDU1297 Children’s Queue
- HDU1438 钥匙计数之一
- HDU1465 ~1466 1480 钥匙计数之二
- HDU2013 蟠桃记
- HDU2018 母牛的故事
- HDU2041~2042
<<<<<<< HEAD - HDU2044~2050 (10/5专题练习)
HDU2563 统计问题
递推
1 | 题目信息转化:当你走完第(n-2)步 |
参考:http://acm.hdu.edu.cn/discuss/problem/post/reply.php?postid=30032&messageid=1&deep=0
1 | /* |
DFS
DFS的话是超时了
1 |
|
之前写了一发错误的dfs,首先是dfs方向走错了,搞混了x,y轴,向下是x轴,向右是y轴。其次是状态回溯搞错了,应该是回溯最后一个点,而不是前一个点,因为最后那一个点可能通过别的方式还会走到。还有一个就是每次走过一个点应该标记,那个标记语句应该放在for里面,放在外面的话
1 |
|
- HDU2044~2050 (10/5专题练习)