zoukankan      html  css  js  c++  java
  • 方案dp。。

        最近经常做到组合计数的题目,每当看到这种题目第一反应总是组合数学,然后要用到排列组合公式,以及容斥原理之类的。。然后想啊想,最后还是不会做。。

        但是比赛完之后一看,竟然是dp。。例如前几天的口号匹配求方案数的题目,今天的uva4656,以及hdu4248都是这种类型的题目。。

        说说uva4565吧。

        题意大概意思是:有N种纸牌,G给位置。。然后给定每种纸牌最少排几张,求满足的方案。

        这样一来我们怎么划分状态呢?以位置?

        不,我们得用纸牌来划分状态,并枚举纸牌之前用了几张

        那么用f[i][j]表示前I个纸牌已经满足题意,且总共放了j个位置的方案数。那么 f[i][j] = sigma(f[i-1][k] * c[G - k][j - k]){j - k >= a[i]}

        至于为什么是 f[i-1][k] * c[G - k][j - k],我们可以这样理解:

                 反正总的位置固定,选取的j-k个在剩下的G-k个里选择位置就行了。。(这样不会有问题吧)

        hdu4248:

          这一题自己懒得写了,转自这个博客http://www.cnblogs.com/sweetsc/archive/2012/07/17/2595189.html

         我觉得写得很不错!

         题意:有N种石头,每种石头有A1,A2....AN个,现取出一些石头组成序列,求可以组成多少种序列

         例如:3种:可以产生:B; G; M; BG; BM; GM; GB; MB; MG; BGM; BMG; GBM; GMB; MBG; MGB.

         我们采用动态规划的思想,划分阶段:按照石头种类划分阶段。于是乎,咱们对于第i种石头,相当于之前石头的颜色并不重要,借助高中数学插板法的思想,假如之前的i - 1 种石头,拼出了长      度为len,那么,相当于有len + 1个空,咱们要放第 i 种石头进去,于是乎,转化成了经典问题,我比较得意的总结:

    球和球 盒和盒 空盒 情况数
    有区别 有区别 有空盒 m^n
    有区别 有区别 无空盒 M!s(n,m)
    有区别 无区别 有空盒 S(n,1)+s(n,2)+…+s(n,m),n>=m
          S(n,1)+s(n,2)+…+s(n,n),n<=m
    有区别 无区别 无空盒 S(n,m)
    无区别 有区别 有空盒 C(n+m-1,n)
    无区别 有区别 无空盒 C(n-1,m-1)
    无区别 无区别 有空盒 DP
    无区别 无区别 无空盒 DP

         这里,第 i 种石头互相没有区别,len + 1个空有序,相当于有区别,可以有空盒,于是,如果咱们从第 i 种中放put个进去,情况数应该是 C(put + len , put)

         于是设计状态:DP[i][j] 表示 用前 i 种石头,排出长度为 j 的可能数

         然后,状态转移的时候,枚举在阶段 i 放入put个,DP[i + 1][j + put] += DP[i][j] * C(put + j, put) 即可

        附上自己奇丑无比的代码:

         Uva4656

    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <set>
    #include <stack>
    #include <cmath>
    #include <vector>
    #include <algorithm>
    #define MXN 50100
    #define Inf 101010
    #define M0(a) memset(a, 0, sizeof(a))
    using namespace std;
    double c[36][36], f[36][36];
    int a[50], sum[50];
    int n, m;
    void init(){
         M0(c);
         for (int i = 0; i <= 33; ++i)
            c[i][0] = 1;
         for (int i = 1; i <= 33; ++i)
            for (int j = 1; j <= i; ++j)
              c[i][j] = c[i-1][j] + c[i-1][j-1];
    }
    
    void solve(){
        M0(sum);
        M0(f);
        scanf("%d%d", &n, &m); 
        for (int i = 1; i <= m; ++i){
            scanf("%d", &a[i]);
            sum[i] = sum[i-1] + a[i];
        }
        f[0][0] = 1;
        for (int i = 1; i <= m; ++i)
           for (int j = sum[i]; j <= n; ++j){
               for (int k = a[i]; k <= j; ++k)
                 f[i][j] += f[i-1][j-k] * c[n - j + k][k];
           }
        for (int i = 1; i <= n; ++i)
          f[m][n] /= m;
        printf("%.6lf
    ", f[m][n] * 100.00);
    }
    
    int main(){
      //   freopen("a.in", "r", stdin);
      //   freopen("a.out","w", stdout);
         int T, cas = 0;
         scanf("%d", &T);
         init();
         for (int i = 1; i <= T; ++i){
              printf("Case #%d: ", i);
              solve(); 
         }
    
     //    fclose(stdin); fclose(stdout);   
    }

    hdu4248

    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <set>
    #include <stack>
    #include <cmath>
    #include <vector>
    #include <algorithm>
    #define MXN 50100
    #define Inf 101010
    #define P 1000000007
    #define M0(a) memset(a, 0, sizeof(a))
    using namespace std;
    int  c[10100][102];
    long long  f[102][10100];
    int n, a[110], m, sum[110];
    
    void init(){
        for (int i = 0; i <= 10010; ++i) 
           c[i][0] = 1;
        for (int i = 1; i <= 10010; ++i)
           for (int j = 1; j <= 101 && j <= i; ++j)
             c[i][j] = (c[i-1][j] + c[i-1][j-1]) % P;
    }
    
    void solve(){
          m = 0;
          M0(f);
          M0(sum);
          for (int i = 1; i <= n; ++i){
              scanf("%d", &a[i]);  
               m += a[i];
               sum[i] = m;  
          }  
          f[0][0] = 1;
          long long ans = 0;
          for (int i = 1; i <= n; ++i)
             for (int j = 0; j <= sum[i]; ++j){
                 for (int k = 0; k <= a[i]; ++k){
                     if (k > j) break;
                     f[i][j] = (f[i][j] + f[i-1][j-k] * c[j][k]) % P;
                 }
                if (i == n && j) ans =  (ans + f[i][j]) % P;
             }
          printf("%I64d
    ", ans);
    }
    
    int main(){
      //freopen("a.in", "r", stdin);
     //    freopen("a.out","w", stdout);
         int T, cas = 0;
         init();
         while (scanf("%d", &n) != EOF){
              printf("Case %d: ", ++cas);
              solve(); 
         }
    
         fclose(stdin); fclose(stdout);   
    }
  • 相关阅读:
    0是字符串的终止符
    c语言中获取数组的长度写法
    c语言第一个程序
    linux下adb连接不上解决方法
    android的apk权限查看
    dumpsys netpolicy中state的含义
    查看ps和dumpsys netpolicy
    批量安装/卸载手机apk--python语言
    【转载】Think as Customer 以客户为中心的测试理念
    利用xampp进行https操作
  • 原文地址:https://www.cnblogs.com/yzcstc/p/3239719.html
Copyright © 2011-2022 走看看