zoukankan      html  css  js  c++  java
  • 动态规划法

      动态规划算法和分治法类似,其基本思想也是将待求解问题分解成若干子问题,但是经分解得到的子问题的数目只是多项式量级,而且子问题不是互相独立的。可以避免分治法中子问题被重复计算很多次。

    两个要素:(1)子问题重叠性质(2)最优子结构性质

    子问题重叠性质:(递归算法求解时)有些子问题被反复计算多次。动态规划法中对每一个子问题只解一次。

    最优子结构性质:当一个问题的最优解包含了其子问题的最优解。

    全源最短路径

    Floyed算法采用自底向上方式计算,dist[i][j]存放从结点i到j的最短路径的长度,在算法的第k步上,做出决策:从i到j的最短路径上是否包含结点k,时间复杂度O(n^3)

    最长公共子序列

    dp[i][j] 表示第一个序列的前i个字符和第二个序列的前j个字符的最长公共子序列的长度,动态转移方程为:

    dp[i][j] = max(dp[i-1][j], dp[i][j-1],dp[i-1][j-1] + (A[i]==B[j] ? 1 : 0)),表示在这三种状态中取到最大值,

    (1)第一种状态表示不录入第一个序列的第i个字符时的最长公共子序列;
    (2)第二种状态表示不录入第二个序列的第j个字符时的最长公共子序列;
    (3)第三种状态表示第一个序列的前i-1个字符与第二个序列前j-1个字符的公共子序列加上最后一个字符的录入状态,如果最后的一个字符相等则录入1,否则为0。
    然后根据动归的状态,来判断我们要求得的序列中的字符有哪些。
    #define INF 0x3f3f3f3f
    char a[1001], b[1001];
    int dp[1001][1001], len1, len2;
    void lcs(int i, int j)
    {
        for(i=1; i<=len1; i++)
        {
            for(j=1; j<=len2; j++)
            {
                if(a[i-1] == b[j-1])///a[i]和b[j]相等
                    dp[i][j] = dp[i-1][j-1] + 1;
                else if(dp[i-1][j] > dp[i][j-1])
                    dp[i][j] = dp[i-1][j];
                else
                    dp[i][j] = dp[i][j-1];
            }
        }
    } 
    
    void llcs()///根据dp求得最长公共子序列
    {
        int i, j, z = 0;
        char c[1001];
        memset(c, 0, sizeof(c));
        i = len1, j = len2;
        while(i!=0 && j!=0)
        {
            if(a[i-1] == b[j-1])
            {
                c[z++] = a[--i];
                j--;
            }
            else if(dp[i-1][j] < dp[i][j-1])
                j--;
            else if(dp[i][j-1] <= dp[i-1][j])
                i--;
        }
        for(i=z-1; i>=0; i--)
            printf("%c", c[i]);
        printf("
    ");
    }
    
    int main()
    {
        while(scanf(" %s", a)!=EOF)
        {
            scanf(" %s", b);
            memset(dp, 0, sizeof(dp));
            len1 = strlen(a);
            len2 = strlen(b);
            lcs(len1, len2);
            llcs();
        }
        return 0;
    }
    最长公共子序列

    0/1背包问题

    0/1背包问题可以看作是决策一个序列(x1, x2, …, xn),对任一变量xi的决策是决定xi=1还是xi=0。在对xi-1决策后,已确定了(x1, …, xi-1),在决策xi时,问题处于下列两种状态之一:

    (1)背包容量不足以装入物品i,则xi=0,背包不增加价值;

    (2)背包容量可以装入物品i,则xi=1,背包的价值增加了vi。

    int v[maxn],w[maxn],dp[maxn];///dp[m]是背包容量为m时能装入的最大价值
    int main()
    {
        int t,i,j,n,m;
        cin>>t;
        while(t--)
        {
           cin>>n>>m;///n是物品数量,m是背包容量
           for(i=1;i<=n;i++)
             cin>>v[i];///价值
           for(i=1;i<=n;i++)
             cin>>w[i];///体积
           memset(dp,0,sizeof(dp));
           for(i=1;i<=n;i++)
           {
              for(j=m;j>=w[i];j--)
                dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
            }
            cout<<dp[m]<<endl;
        }
    }
    0/1背包

    完全背包

    有n种物品和一个容量为v的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大

    int dp[50010];
    int w[2010],v[2010];///w是物品体积,v是物品价值
    int main
    {
      int i,j,t,n,m,a,b;
      scanf("%d",&t);
      while(t--)
      {
         scanf("%d%d",&n,&m);///n是物品数量,m是背包容量
         for(i=1;i<=n;i++)
           scanf("%d"%d",&w[i],&v[i]);
         memset(dp,-inf,sizeof(dp));
         dp[0]=0;
         for(i=1;i<=n;i++)
         {
            for(j=w[i];j<=m;j++)
              dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
         }
         if(dp[m]>0) printf("%d
    ",dp[m]);
         else printf("No
    ");
      }  
      return 0;
    }
    完全背包

     与01背包相比,完全背包只是第二重循环的顺序发生了改变。保证用较小容量的最大价值去更新较大容量的最大价值,不重不漏。

    越努力越幸运!
  • 相关阅读:
    Ibatis,Spring整合(注解方式注入)
    Amoeba搞定mysql主从读写分离
    ClickjackFilterDeny X-Frame-Options
    Clickjacking: X-Frame-Options header missing
    升级至Struts2 2.5.2
    js和java MD5加密
    Struts2 2.5.2
    Struts2 2.5.2的套路
    java8 集合流式操作
    JS 文本输入框放大镜效果
  • 原文地址:https://www.cnblogs.com/Littlejiajia/p/13365900.html
Copyright © 2011-2022 走看看