zoukankan      html  css  js  c++  java
  • 蓝桥杯 未名湖边的烦恼 递推 dp

    问题描述
      每年冬天,北大未名湖上都是滑冰的好地方。北大体育组准备了许多冰鞋,可是人太多了,每天下午收工后,常常一双冰鞋都不剩。
      每天早上,租鞋窗口都会排起长龙,假设有还鞋的m个,有需要租鞋的n个。现在的问题是,这些人有多少种排法,可以避免出现体育组没有冰鞋可租的尴尬场面。(两个同样需求的人(比如都是租鞋或都是还鞋)交换位置是同一种排法)
    输入格式
      两个整数,表示m和n
    输出格式
      一个整数,表示队伍的排法的方案数。
    样例输入
    3 2
    样例输出
    5
    数据规模和约定
    m,n∈[0,18]
    解法一:递归。f(m,n)函数用于返回还鞋人数为m,租鞋人数为n时的合法方案数。main函数里读入还鞋人数m,租鞋人数n,直接调用f(m,n)函数。

      由题意,需要保证,当前队伍内,任意前i个必有还鞋人数大于等于租鞋人数,因为“每天下午收工后,常常一双冰鞋都不剩”,所以当天队伍第一位一定是还鞋,然后第二位可能是还鞋也可能是租鞋,如果第二位是还鞋的话,和第一位是还鞋的情况相同,如果第二位是租鞋的话下一位只能是还鞋,下一位是还鞋的话,又和第一位是还鞋的情况相同。符合递归的的基本思想:复杂问题可以分解为相同的子问题。

      本题用递归实现比较简单,从后往前考虑,只考虑当前队伍最后一位应该放置还鞋还是租鞋。

      最后一位放置还鞋的话,总还鞋人数减一,考虑队伍最后一位除外的前面所有人合法的方案数,为f(m - 1, n);同理,最后一位放置租鞋的话,合法的方案数为f(m, n - 1);两种情况相加即为最后一位放还鞋或租鞋的总方案数。

      边界情况:当队伍里还鞋人数m小于租鞋人数n时,无论如何摆放,都不合法,返回0;当队伍里的租鞋人数为0时,因为“两个同样需求的人(比如都是租鞋或都是还鞋)交换位置是同一种排法”,所以合法方案数只有1种。

      

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 int f(int m, int n) {
     4     if (m < n) {
     5         return 0;
     6     } else if (n == 0) {
     7         return 1;
     8     } else {
     9         return f(m - 1, n) + f(m, n - 1);
    10     }
    11     
    12 }
    13 int main() {
    14     int m, n;
    15     cin >> m >> n;
    16     cout << f(m, n) << endl;
    17     return 0;
    18 }

    解法二:动态规划。从集合角度考虑DP问题参考自闫氏DP分析法https://www.bilibili.com/video/BV1X741127ZM?from=search&seid=9453608681194784228

    先看一段错误代码

     1 //错误代码 
     2 #include <bits/stdc++.h>
     3 using namespace std;
     4 const int N = 20;
     5 int dp[N][N];
     6 int main() {
     7     int m, n;
     8     cin >> m >> n;
     9     for (int i = 1; i <= m; i++) { //初始化,所有租鞋人数为0时,合法的方案数为1 
    10         dp[i][0] = 1;
    11     }
    12     for (int i = 1; i <= m; i++) {
    13         for (int j = 1; j <= n; j++) { 
    14             dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
    15         }
    16     }
    17     cout << dp[m][n] << endl;
    18     return 0;
    19 }

    正解为5,输出了6。

    输出dp[i][j]里的值debug一下

     1 //错误代码 
     2 #include <bits/stdc++.h>
     3 using namespace std;
     4 const int N = 20;
     5 int dp[N][N];
     6 int main() {
     7     int m, n;
     8     cin >> m >> n;
     9     for (int i = 1; i <= m; i++) { //初始化,所有租鞋人数为0时,合法的方案数为1 
    10         dp[i][0] = 1;
    11     }
    12     for (int i = 1; i <= m; i++) {
    13         for (int j = 1; j <= n; j++) { 
    14             dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
    15         }
    16     }
    17     //输出dp[i][j]的内容 
    18     for (int i = 1; i <= m; i++) {
    19         for (int j = 1; j <= n; j++) {
    20             cout << "i:" << i << " " << "j:" << j << " " << "dp[i,j]:" << dp[i][j] << endl;
    21         }
    22     } 
    23     return 0;
    24 }

    输出结果为:

    依次分析:

    i=1,j=1时,一人还鞋,一人租鞋,合法方案数为1,没毛病

    i=1,j=2时,一人还鞋,两人租鞋,合法方案数为1。发现错误,由上面分析,当还鞋人数小于租鞋人数时,此时没有合法方案。

    更改后的代码

     1 //正确代码 
     2 #include <bits/stdc++.h>
     3 using namespace std;
     4 const int N = 20;
     5 int dp[N][N];
     6 int main() {
     7     int m, n;
     8     cin >> m >> n;
     9     for (int i = 1; i <= m; i++) {
    10         dp[i][0] = 1;
    11     }
    12     for (int i = 1; i <= m; i++) {
    13         for (int j = 1; j <= i; j++) { //此处更改为j <= i 
    14             dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
    15         }
    16     }
    17     //输出dp[i][j]的内容 
    18     /*for (int i = 1; i <= m; i++) {
    19         for (int j = 1; j <= i; j++) {
    20             cout << "i:" << i << " " << "j:" << j << " " << "dp[i,j]:" << dp[i][j] << endl;
    21         }
    22     } */
    23     cout << dp[m][n] << endl;
    24     return 0;
    25 }

    更改后的代码输出dp[i][j]的值

     

     即要保证对于每个i,对应的j<=i,确保每个dp[i][j]里存的都是正确的值

     

  • 相关阅读:
    continue用法
    break用法
    VLAN的划分
    子网掩码的计算
    简述RIP路由协议和OSPF路由协议的相同点和不同点。
    工程监理的内容是什么?
    工程监理的意义和职责是什么?
    双绞线测试的参数主要有哪些?
    光纤熔接损耗原因?
    综合布线系统的设计等级有哪几种?各有什么特点?
  • 原文地址:https://www.cnblogs.com/fx1998/p/12591341.html
Copyright © 2011-2022 走看看