• 读书笔记


    当然,还有LIS, 不过之前总结过了,这次就不贴了

    /**
    给定两个字符串s1s2s3...sn和t1t2t3...tm
    求这两个字符串最长的公共子序列的长度
    
    dp[i][j]表示s序列考虑si,t序列考虑tj时的最长公共子序列
    
    状态转移方程:
    s[i] == t[j] : dp[i][j] = dp[i-1][j-1] + 1
    s[i] != t[j] : dp[i][j] = max(dp[i-1][j], dp[i][j-1])
    */
    
    #include <iostream>
    #include <stdio.h>
    #include <string.h>
    using namespace std;
    const int MAXN = 1000 + 5;
    
    char s[MAXN], t[MAXN];
    int dp[MAXN][MAXN];
    
    int solve()
    {
        scanf("%s%s", s + 1, t + 1);
        int n = strlen(s + 1);
        int m = strlen(t + 1);
        memset(dp, 0, sizeof(dp));
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= m; j++)
            {
                if(s[i] == t[j])
                    dp[i][j] = dp[i-1][j-1] + 1;
                else
                    dp[i][j] = max(dp[i][j-1], dp[i-1][j]);
            }
        }
        printf("%d
    ", dp[n][m]);
        return dp[n][m];
    }
    
    int main()
    {
        solve();
        return 0;
    }
    /**
    有n种不同大小的数字,每种mi个,判断是否可以从这些数字中选出若干个
    让他们的和恰好为K
    
    问题规模:
     1 <= n <= 100
     1 <= ai,mi <= 1e5
     1 <= K <= 1e5
    
     思路一:
     dp[i][j] 表示前i种物品挑选若干个是否可以组成和是j
     状态转移方程: dp[i][j] = dp[i][j] | dp[i-1][j - k*ai] ( j >= k*ai, 0 <= k <= mi)
     复杂度O(K*sum(mi)) 还是比较高的
    
     思路二:
     dp[i][j] 表示前i中物品加和得到j的情况下,mi个ai最多能剩多少个,如果不能得到j,值为-1
     状态转移方程:
    
     dp[i][j] = mi, if  dp[i-1][j] >= 0
     dp[i][j] = -1, if  j < ai || dp[i][j - ai] <= 0
     dp[i][j] = dp[i][j - ai] + 1, else ...
    
     复杂度是O(nK)
    */
    #include <iostream>
    #include <stdio.h>
    #include <string.h>
    using namespace std;
    const int MAXK = 1e5 + 5;
    const int N = 100 + 5;
    int n, K;
    int m[N], a[N];
    int dp1[N][MAXK];
    int dp2[MAXK];
    
    void solve1()
    {
        memset(dp1, 0, sizeof(dp1));
        dp1[0][0] = 1;
        for(int i = 1; i <= n; i++)
        {
            for(int j = 0; j <= K; j++)
            {
                for(int k = 0; k <= m[i]; k++)
                {
                    if(j - k * a[i] >= 0)
                        dp1[i][j] |= dp1[i-1][j - k * a[i]];
                }
            }
        }
        if(dp1[n][K]) printf("Yes
    ");
        else printf("No
    ");
    }
    
    void solve2()
    {
        memset(dp2, -1, sizeof(dp2));
        dp2[0] = 0;
        for(int i = 1; i <= n; i++)
        {
            for(int j = 0; j <= K; j++)
            {
                if(dp2[j] >= 0)
                    dp2[j] = m[i];
                else if(j < a[i] || dp2[j - a[i]] <= 0)
                    dp2[j] = -1;
                else
                    dp2[j] = dp2[j - a[i]] + 1;
            }
        }
        if(dp2[K] >= 0) printf("Yes
    ");
        else printf("No
    ");
    }
    
    
    int main()
    {
        scanf("%d%d", &n, &K);
        for(int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        for(int j = 1; j <= n; j++)
            scanf("%d", &m[j]);
        solve1();
        //solve2();
        return 0;
    }
    /**
    有n个无区别的物品,将它们划分成不超过m组,求划分方法的个数,对M取模
    问题规模:
    1 <= m <= n <= 1000
    2 <= M <= 10000
    
    DP方法:
    dp[i][j] 表示j的i划分.
    j的i划分,将j划分成A1,A2,...,Ai,那么在每一个A中取出一个,就变成了
    j-i的i划分。
    如果Ak == 0,那么就成了j的i-1划分了
    所以有递推式
    dp[i][j] = dp[i][j-i] + dp[i-1][j];
    */
    #include <iostream>
    #include <stdio.h>
    #include <string.h>
    using namespace std;
    const int N = 1000 + 5;
    int n, m;
    int dp[N][N];
    
    void solve()
    {
        dp[0][0] = 1;
        for(int i = 1; i <= m; i++)
        {
            for(int j = 1; j <= n; j++)
            {
                if(j - i >= 0)
                    dp[i][j] = dp[i-1][j];
                else
                    dp[i][j] = (dp[i-1][j] + dp[i][j-i]) % M;
            }
        }
        printf("%d
    ", dp[m][n]);
    }
    
    int main()
    {
        scanf("%d%d", &n, &m);
        solve();
        return 0;
    }
    /**
    多重集组合数问题
    有n种物品,第i种物品有ai个。不同种类的物品可以相互区分,但是同种类的物品是无法区分的
    从这些物品中取出m个的话,有多少种取法,方法数模M
    问题规模:
    1 <= n <= 1000
    1 <= m <= 1000
    1 <= ai <= 1000
    2 <= M <= 10000
    
    //本题的方法学到了一种化简的思路
    动态规划法:
    dp[i][j] 表示的是考虑第i种物品,取出了j个的取法数
    得到状态转移方程:
    dp[i][j] = sum(dp[i-1][j-k]) ,0 <= k <= ai 且 j >= k
    当然,这样的转移方程很容易得到,求解也很容易,但是需要三重循环。
    时间复杂度是O(sum(ai)*m),这题应该计算量大概是1e9的数量级,有可能会卡过,但是很玄
    这个时候需要来化简这个转移方程
    sum(dp[i-1][j-k]) , 0 <= k <= min(j, ai)
    sum(dp[i-1][j-k]) = sum(dp[i-1][j-1 - k]) + dp[i-1][j] - dp[i-1][j-1 - ai], 0 <= k <= min(j-1, ai)
    这样就有 dp[i][j] = dp[i][j-1] + dp[i-1][j] - dp[i-1][j-1 - ai]
    复杂度就变成O(nm),完全可以应付
    */
    #include <iostream>
    #include <string.h>
    #include <stdlib.h>
    #include <stdio.h>
    using namespace std;
    const int INF = 0x3f3f3f3f;
    typedef long long LL;
    
    const int N = 1000 + 5;
    const int M = 1000 + 5;
    
    int n, m;
    int a[N];
    int dp[N][MAXM];
    int M;
    
    int read()
    {
        scanf("%d%d%d", &n, &m, &M);
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
        }
    }
    
    void solve()
    {
        for(int i = 0; i <= n; i++)
            dp[i][0] = 1;
        for(int i = 1; i <= n; i++)
        {
            for(int j = 0; j <= m; j++)
            {
                if(j - 1 -a[i] >= 0)
                    dp[i][j] = (dp[i][j-1] + dp[i-1][j] - dp[i-1][j - 1 - a[i]] + M) % M;
                else
                    dp[i][j] = (dp[i][j-1] + dp[i-1][j]) % M;
            }
        }
        printf("%d
    ", dp[n][m]);
    }
    
    int main()
    {
        solve();
        return 0;
    }
    如果有错误,请指出,谢谢
  • 相关阅读:
    大学毕业4年-回顾和总结(1)-钱,金钱观
    VR开发中性能问题—OculusWaitForGPU
    动态规划(一)
    POJ 2229 Sumsets
    hihoCoder #1122 : 二分图二•二分图最大匹配之匈牙利算法
    hihoCoder #1127 : 二分图二·二分图最小点覆盖和最大独立集
    hihoCoder #1121 : 二分图一•二分图判定
    HDU 1978 How many ways
    POJ1579 Function Run Fun
    POJ 2081 Recaman's Sequence
  • 原文地址:https://www.cnblogs.com/Alruddy/p/7191683.html
Copyright © 2020-2023  润新知