动态规划例题(有趣的数字 + 放苹果)
参考:
https://www.bilibili.com/video/BV1DX4y157r2
一,有趣的数字
1,链接:http://118.190.20.162/view.page?gpid=T2
2,dp 求解
函数关系:
对于第 i 位的状态,根据它前面所出现的数字,总共可列出有 6 种状态。而对于第 i 位的状态的情况就可以根据 第 i-1 位的状态推出。
① 第 i 位及其之前所有位 只存在 2
② 第 i 位及其之前所有位 只存在 2 0
③ 第 i 位及其之前所有位 只存在 2 3
④ 第 i 位及其之前所有位 只存在 2 0 1
⑤ 第 i 位及其之前所有位 只存在 2 3 0
⑥ 第 i 位及其之前所有位 只存在 2 3 0 1
若 第 i 位 为 ① 状态,则只能是 i-1 位 的 ① 状态 +2,这一种情况
若 第 i 位 为 ② 状态,可能是 i-1 位 的 ① 状态 +0 或 i-1 位 的 ②状态 +0 或 i-1 位 的 ②状态 +2 ,这三种情况
若 第 i 位 为 ③ 状态,可能是 i-1 位 的 ① 状态 +3 或 i-1 位 的 ③状态 +3,这两种情况
若 第 i 位 为 ④ 状态,可能是 i-1 位 的 ② 状态 +1 或 i-1 位 的 ④状态 +1 或 i-1 位 的 ②状态 +2,这三种情况
若 第 i 位 为 ⑤ 状态,可能是 i-1 位 的 ② 状态 +3 或 i-1 位 的 ③状态 +0 或 i-1 位 的 ⑤状态 +0 或 i-1 位 的 ⑤状态 +3,这四种情况
若 第 i 位 为 ⑥ 状态,可能是 i-1 位 的 ④ 状态 +3 或 i-1 位 的 ⑤状态 +1 或 i-1 位 的 ⑥状态 +0 或 i-1 位 的 ⑤状态 +3,这四种情况
于是,我们就得到第 i 位包含 2 3 0 1 这四种数字的所有可能。
函数出口:
最高位只能为 2 ,最高位只存在 ①状态,所以当 i 为 1 时 ① 状态只有一种情况 ,其他状态 为 0
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #define N 1002 #define MOD 1000000007 #define ll long long ll dp[N][7]; // 6种状态 ll fun(ll n, ll m) // n 为位数,m 为第几种状态 { if (m == 1) return 1; if (n == 1) return 0; if (m == 2) return (fun(n - 1, 1) + fun(n - 1, 2) * 2) % MOD; if (m == 3) return (fun(n - 1, 1) + fun(n - 1, 3)) % MOD; if (m == 4) return (fun(n - 1, 2) + fun(n - 1, 4) * 2) % MOD; if (m == 5) return (fun(n - 1, 2) + fun(n - 1, 3) + fun(n - 1, 5) * 2) % MOD; if (m == 6) return (fun(n - 1, 4) + fun(n - 1, 5) + fun(n - 1, 6) * 2) % MOD; } void fun2(int n) { for (int i = 1; i <= n; i++) { dp[i][1] = 1 % MOD; dp[i][2] = (dp[i - 1][1] + dp[i - 1][2] * 2) % MOD; dp[i][3] = (dp[i - 1][1] + dp[i - 1][3]) % MOD; dp[i][4] = (dp[i - 1][2] + dp[i - 1][4] * 2) % MOD; dp[i][5] = (dp[i - 1][2] + dp[i - 1][3] + dp[i - 1][5] * 2) % MOD; dp[i][6] = (dp[i - 1][4] + dp[i - 1][5] + dp[i - 1][6] * 2) % MOD; } } int main(void) { int n; scanf("%d", &n); // printf("%lld\n", fun(n, 6)); // 超时 fun2(n); printf("%lld\n", dp[n][6]); system("pause"); return 0; }
3,组合计数
设 0 1 的总个数为 k,则 2 3 的总个数为 n-k。
① n 个数中,k 个 0 1 的选法
且由于 0 不能放在首位,而 1 要在 0 之后,所以 1 也不能放在首位。那么对于 n 个数,其中共有 n-1 个数可供 k 个 0 1 存放,即 c( n-1, k)
② k 个 0 1 中,0 1 不同的选择
设 0 的个数为 t,则 1 <= t <= k-1
由于 0 总在 1 之前,所以 0 总是连续的排在 1 前面,即对于每个 t,0 1 只有 1 中选法。
而 t 共有 k-1 种可能,所以总共有 k-1 种选择。
③ n - k 个 2 3 中,2 3 不同的选择
设 2 的个数为 t,则 1 <= t <= n -k-1
由于 2 总在 3 之前,所以 2 总是连续的排在 3 前面,即对于每个 t,2 3 只有 1 中选法。
而 t 共有 n-k-1 种可能,所以总共有 n-k-1 种选择。
综上,总共的选法为上述可能相乘。
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #define N 1002 #define MOD 1000000007 #define ll long long ll c[N][N]; int main(void) { int n; scanf("%d", &n); for (int i = 0; i <= n; i++) for (int j = 0; j <= i; j++) if (j == 0) c[i][j] = 1; else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % MOD; int rs = 0; for (int i = 2; i <= n; i++) rs = (rs + c[n - 1][i] * (i - 1)*(n - i - 1)) % MOD; printf("%lld\n", rs); system("pause"); return 0; }
二,放苹果:
1,问题:
把 M 个同样的苹果放在 N 个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K 表示)5,1,1 和 1,5,1 是同一种分法。
2,推导
① 函数关系式 f(n, m)
当 m > n 时,f(n, m) = f(n, n)
当 n >= m 时
有篮子为空时,则至少一个篮子为空
f(n, m-1)
无篮子为空时,则一个篮子拿掉一个苹果
f(n-m, m)
② 递归结束条件
无苹果时 0
无篮子时 1
其中,不存在既无苹果,又无篮子的情况。
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> int fun(int n, int m) // 苹果数 盘子数 { if (n == 0) // 没有苹果 return 1; if (m == 0) // 没有盘子 return 0; if (m > n) // 盘子比苹果多 (一定有篮子为空) return fun(n, n); else // 分两种,有无篮子为空 return fun(n, m - 1) + fun(n - m, m); } int main(void) { int a[15][15] = { 0 }; for (int i = 1; i < 15; i++) { a[0][i] = 1; // 0个苹果 a[i][0] = 0; // 0个篮子 } for (int n = 1; n < 15; n++) { for (int m = 1; m < 15; m++) { if (m > n) a[n][m] = a[n][n]; else a[n][m] = a[n][m - 1] + a[n - m][m]; } } int M, N, k; printf("请输入有几个苹果并回车:"); scanf("%d", &M); printf("请输入有几个篮子并回车:"); scanf("%d", &N); //k = a[M][N]; //动态规划 k = fun(M, N); // 递归 printf("%d\n", k); system("pause"); return 0; }
=========== ======== ======== ======= ===== ===== ==== === == =
面对强大的对手,明知不敌,也要毅然亮剑,即使倒下,也要化成一座山,一道岭。