Codeforces382E. Ksenia and Combinatorics【动态规划】

Codeforces382E. Ksenia and Combinatorics

动态规划


题目飞机票
维基百科对树的最大匹配的解释


(๑•́ ₃ •̀๑)卡题面的题真是恶心(打CF的那么多中国人,就是没有中文题面,哼(▔^▔))
最开始还以为要求最大匹配,想E题怎么这么简单,结果让你求方案。。

给你一棵树的节点个数 N (没有给你树的结构),然后让你求最大匹配为M的树有多少个,节点还有编号,编号不同也算不同的两棵树,答案对 109+7 取模

CF没有部份分,就算有,我也不知道这道题朴素怎么写,太难了,后来讨论下来才知道。
我先说说怎么求一棵树的最大匹配(神犇跳过这段),状态为 f[i][0/1] ,表示以 i 为根的树选根或者不选的最大匹配,转移方程为:

  • f[i][0]=f[u][1](ui)

    • f[i][1]=max(f[leftson][0]+f[rightson][1]+1,f[leftson][1]+f[rightson][0]+1)
    • 那么根据这个我们给出这道题的状态:
      f[i][j][k] 表示 i 个节点的树,不选根的最大匹配为j,选根最大匹配为 k 的方案数,但是我们发现,选根最多只会比不选根多1,很好证明,就不多说,所以我们定义f[i][j][0/1],表示 i 个节点的树,不选根的最大匹配为j,选根最大匹配是否多1的方案数,是否多1?所以我们就要枚举很多情况,因为还要考虑编号问题,所以需要用组合数来预处理一下。
      下面是方程的转移(一些细节在代码里面能体现):

      • f[i][j][0]=k=0i/2p=0k/2f[k][p][1]f[ik1][jp][1]Cki1k(ik1)
      • f[i][j][1]=k=0i/2p=0k/2(f[k][p][1]f[ik1][jp1][0]+f[k][p][0]f[ik1][jp1][1]

        +f[k][p][0]f[ik1][jp1][0])Cki1k(ik1)

      这个转移还有很多细节,类似于左右儿子节点相同时要除以2(不能直接除,因为要取模,根据组合数的推倒间接除以2),当左儿子数等于0的时候就不要乘以0(即: Cki1k(ik1) 要改为 Cki1(ik1) ),所以,直接上代码(~o▔▽▔)~o:

      #include <iostream>
      #include <cstdio>
      #include <cstdlib>
      using namespace std;
      
      typedef long long lld;
      
      const int maxn = 53, mod = 1e9 + 7;
      int n, m;
      lld f[maxn][maxn][2], C[maxn][maxn];
      
      void initForComb() {
          C[0][0] = 1ll;
          for(int i = 1; i <= n; i++) {
              C[i][0] = 1ll;
              for(int j = 1; j <= i; j++)
                  C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
          }
      }
      
      void add(lld &a, lld b, lld c, lld d) {
          b *= c; if(b >= mod) b %= mod;
          b *= d; if(b >= mod) b %= mod;
          a += b; if(a >= mod) a -= mod;
      }
      
      int main() {    
          scanf("%d%d", &n, &m);
      
          if(n <= 1) {
              printf("0\n");
              return 0;
          }
      
          initForComb();
      
          f[1][0][0] = 1; f[0][0][1] = 1;
          for(int i = 2; i <= n; i++) {
              for(int j = 1; j <= i / 2; j++) {
                  for(int k1 = 0, k2 = i - 1; k1 <= k2; k1++, k2--) {
                      for(int p = 0; p <= j; p++) {
                          lld CC;
                          //避免算重复,当左儿子个数 = 右儿子个数时,要除以2
                          if(k1 != k2) CC = C[i - 1][k1] ;
                          else CC = C[i - 2][k1 - 1];
                          if(k1) CC = CC * k1 % mod * k2 % mod;
                          else CC = CC * k2 % mod;
                          add(f[i][j][0], f[k1][p][1], f[k2][j - p][1], CC);
                          add(f[i][j][1], f[k1][p][1], f[k2][j - p - 1][0], CC);
                          add(f[i][j][1], f[k1][p][0], f[k2][j - p - 1][1], CC);
                          add(f[i][j][1], f[k1][p][0], f[k2][j - p - 1][0], CC);
                      }
                  }
              }
          }
      
          printf("%lld\n", (f[n][m][0] + f[n][m][1]) % mod);
      
          return 0;
      }
Codeforces的动态规划题单中,有基础DP、优化递推式、进阶DP、数据结构、优先队列、并查集、图论、最短路、最小生成树、数论、二分搜索等不同类型的题目。 代码中的内容是一个动态规划的例子,它采用了一个二维数组来存储中间结果,并通过递推的方式计算最优解。这个例子中,它使用了一个for循环嵌套来遍历数组,并利用状态转移方程更新数组中的值。最后输出的是计算得到的最优解。 要注意的是,这段代码是一个完整的程序,需要依赖于一些特定的输入数据才能正确运行。如果你想在Codeforces上找到更多的动态规划题目,可以访问它们的官方网站并浏览题库。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [牛客练习_21314:codeforces (动态规划+01背包)](https://blog.csdn.net/qq_45750296/article/details/109587967)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [leetcode双人赛-acm-challenge-workbook:acm-挑战-工作簿](https://download.csdn.net/download/weixin_38701340/19923844)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Codeforces Round #750 (Div. 2)E题(动态规划)](https://blog.csdn.net/m0_51506743/article/details/121083708)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值