• 动态规划总专题


     

    POJ 1458

        LCS。

    View Code
    #include <stdio.h>
    #include <string.h>
    #define N 1001
    char a[N], b[N];
    int dp[N][N];
    
    int max(int x, int y){return x > y ? x : y;}
    
    int solve()
    {
        int n = strlen(a), m = strlen(b);
        int i, j;
        for(i = 0; i <= n; i ++)
            dp[i][0] = 0;
        for(j = 0; j <= m; j ++)
            dp[0][j] = 0;
        for(i = 1; i <= n; i ++)
            for(j = 1; j <= m; j ++)
                if(a[i - 1] == b[j - 1])
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                else
                    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
        return dp[n][m];
    }
    
    int main()
    {
        while(scanf("%s%s", a, b) != EOF)
            printf("%d
    ", solve());
        return 0;
    }

    POJ 2250

        LCS,只不过字母比较换成了字符串比较,输出要花点时间,算法导论上有很详细的讲解。

    View Code
    #include<iostream>
    #include<string>
    #include<vector>
    using namespace std;
    vector<string> a,b;
    int c[101][101];
    short d[101][101];
    int lena,lenb;
    
    void run()
    {
        lena = a.size();
        lenb = b.size();
        for(int i=0;i<101;i++)
            c[i][0] = c[0][i] = 0;
        for(int i=1;i<=lena;i++)
        {
            for(int j=1;j<=lenb;j++)
            {
                if(a[i-1]==b[j-1])
                {
                    c[i][j]=c[i-1][j-1]+1;
                    d[i-1][j-1]=0;
                }
                else if(c[i-1][j]>c[i][j-1])
                {
                    c[i][j]=c[i-1][j];
                    d[i-1][j-1]=1;
                }
                else
                {
                    c[i][j]=c[i][j-1];
                    d[i-1][j-1]= -1;
                }
            }
        }
    }
    
    void print_lcs(int x,int y)
    {
        if(x<0||y<0)return;
        if(d[x][y]==0)
        {
            print_lcs(x-1,y-1);
            if(x==0||y==0)
                cout<<a[x];
            else cout<<" "<<a[x];
        }
        else if(d[x][y]==1)
            print_lcs(x-1,y);
        else print_lcs(x,y-1);
    }
    
    
    int main()
    {
        string tmp;
        while(cin >> tmp)
        {
            a.clear();
            b.clear();
            do
            {
                a.push_back(tmp);
            }while(cin >> tmp&&tmp!="#");
            while(cin>>tmp&&tmp!="#")
            {
                b.push_back(tmp);
            }
            run();
            print_lcs(lena-1,lenb-1);
            cout<<endl;
        }
        return 0;
    }

    POJ 1159

        将字符串反转后和原字符串做LCS就差不多了。

    View Code
    #include<iostream>
    #include<string>
    #include<algorithm>
    using namespace std;
    const int maxn = 5001;
    short c[maxn][maxn];
    int n;
    string a,b;
    
    void lcs()
    {
        for(int i=0;i<n;i++)
            c[i][0]=c[0][i]=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(a[i-1]==b[j-1])
                    c[i][j]=c[i-1][j-1]+1;
                else if(c[i-1][j]>c[i][j-1])
                    c[i][j] = c[i-1][j];
                else c[i][j] = c[i][j-1];
            }
        }
    }
    int main()
    {
        while(cin >> n)
        {
            cin >> a;
            b = a;
            reverse(b.begin(),b.end());
            lcs();
            cout<<n-c[n][n]<<endl;
        }
        return 0;
    }

    POJ 1887

        最长下降子序列。

        转移方程:dp[i] = max(dp[i], dp[j] + 1)(j = 0...i-1 && v[i] <= v[j]),时间复杂度O(n^2)。

    View Code
    #include <cstdio>
    #include <cstring>
    int n, a[100001], dp[100001];
    inline int max(int x, int y)
    {
        return x > y ? x : y;
    }
    
    int solve()
    {
        int i, j, m = 1;
        dp[0] = 1;
        for(i = 1; i < n; i ++)
        {
            dp[i] = 1;
            for(j = 0; j < i; j ++)
            {
                if(a[i] <= a[j])
                    dp[i] = max(dp[i], dp[j] + 1);
            }
            if(dp[i] > m)
                m = dp[i];
        }
        return m;
    }
    
    int main()
    {
        int v, cas = 1;
        while(scanf("%d", &v), v != -1)
        {
            n = 0;
            do
            {
                a[n ++] = v;
            }while(scanf("%d", &v), v != -1);
            if(cas != 1)
                printf("
    ");
            printf("Test #%d:
    ", cas ++);
            printf("  maximum possible interceptions: %d
    ", solve());
        }
        return 0;
    }

        用“贪心 + 二分”可以达到nlogn,维护dp为递减序列,不断更新dp序列的某个位置为在该位置可能出现的最大值,如果要插入的v值小于该递减序列最小值,则将v插入到队尾。

    View Code
    #include <cstdio>
    #define N 50001
    int n, m, dp[N];
    
    int bs(int v)
    {
        int l = 0, r = m;
        while(l < r)
        {
            int mid = (l + r) >> 1;
            if(dp[mid] >= v)
                l = mid + 1;
            else
                r = mid;
        }
        return l;
    }
    
    int main()
    {
        int v, cas = 1;
        while(scanf("%d", &v), v != -1)
        {
            m = 0;
            do
            {
                if(m == 0)
                    dp[m ++] = v;
                else
                {
                    int k = bs(v);
                    if(k == m)
                        dp[m ++] = v;
                    else   
                        dp[k] = v;
                }
            }while(scanf("%d", &v), v != -1);
            if(cas != 1) 
                putchar('
    ');
            printf("Test #%d:
    ", cas ++);
            printf("  maximum possible interceptions: %d
    ", m);
        }
        return 0;
    }

    POJ 1631

        看出是LIS差不多已经赢了,而此题基数大必须nlogn,把上面POJ1887题的nlogn代码复制过来稍微改改就行了。

    View Code
    #include <cstdio>
    #define N 40001
    int m, q[N];
    
    int bs(int v)
    {
        int l = 0, r = m;
        while(l < r)
        {
            int mid = (l + r) >> 1;
            if(q[mid] < v)
                l = mid + 1;
            else
                r = mid;
        }
        return r;
    }
    
    int main()
    {
        int n, p, v;
        scanf("%d", &n);
        while(n --)
        {
            scanf("%d", &p);
            m = 0;
            while(p --)
            {
                scanf("%d", &v);
                if(m == 0)
                    q[m ++] = v;
                else
                {
                    int k = bs(v);
                    if(k == m)
                        q[m ++] = v;
                    else
                        q[k] = v;
                }
            }
            printf("%d
    ", m);
        }
        return 0;
    }

    POJ 3624

        没什么好讲的,01背包。

    View Code
    #include <cstdio>
    #include <cstring>
    #define MAXN 13000
    int W[MAXN], D[MAXN], dp[MAXN];
    int N, M;
    
    int max(int a, int b)
    {
        return a > b ? a : b;
    }
    
    int CharmBracelet()
    {
        memset(dp, 0, sizeof(dp));
        for(int i = 0; i < N; i ++)
        {
            for(int j = M; j >= W[i]; j --)
            {
                dp[j] = max(dp[j], dp[j - W[i]] + D[i]);
            }
        }
        return dp[M];
    }
    
    int main()
    {
        while(scanf("%d%d", &N, &M) != EOF)
        {
            for(int i = 0; i < N; i ++)
            {
                scanf("%d%d", &W[i], &D[i]);
            }
            printf("%d
    ", CharmBracelet());
        }
        return 0;
    }

    POJ 2192

        看str1的前i个字母和str2的前j个字母能否组成str的前i+j个字母。

    View Code
    #include <stdio.h>
    #include <string.h>
    #define maxn 205
    char a[maxn], b[maxn], c[maxn << 1];
    bool dp[maxn][maxn];
    
    bool solve()
    {
        int i, j, n = strlen(a), m = strlen(b);
        for(i = 0; i < n; i ++)
            if(a[i] == c[i])
                dp[i + 1][0] = true;
        for(i = 0; i < m; i ++)
            if(b[i] == c[i])
                dp[0][i + 1] = true;
        dp[0][0] = true;
        for(i = 1; i <= n; i ++)
        {
            for(j = 1; j <= m; j ++)
            {
                if((dp[i - 1][j] && a[i - 1] == c[i + j - 1]) || (dp[i][j - 1] && b[j - 1] == c[i + j - 1]))
                    dp[i][j] = true;
                else
                    dp[i][j] = false;
            }
        }
        return dp[n][m];
    }
    
    int main()
    {
        int n, i;
        scanf("%d", &n);
        for(i = 1; i <= n; i ++)
        {
            scanf("%s%s%s", a, b, c);
            if(solve())
                printf("Data set %d: yes
    ", i);
            else
                printf("Data set %d: no
    ", i);
        }
        return 0;
    }

    ZOJ 3631

         这题由于基数M很大,无法用传统意义上的背包,但总体思想还是背包,实现可以用dfs+剪枝。

    View Code
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 10000001
    
    int a[31], ans;
    int n, m;
    
    int max(int x, int y){return x > y ? x : y;}
    
    bool cmp(int x, int y){return x > y;}
    
    void dfs(int d, int s)
    {
        if(ans == m) return;
        if(s > m) return ;
        if(d >= n)
        {
            ans = max(ans, s);
            return ;
        }
        int sum = s;
        for(int i = d; i < n; i ++)  // 剪枝
            sum += a[i];
        if(sum < ans)
            return ;
        dfs(d + 1, s + a[d]);
        dfs(d + 1, s);
    }
    
    int main()
    {
        int i;
        while(scanf("%d%d", &n, &m) != EOF)
        {
            int sum = 0;
            for(i = 0; i < n; i ++)
            {
                scanf("%d", &a[i]);
                sum += a[i];
            }
            if(sum <= m)
            {
                printf("%d
    ", sum);
                continue;
            }
            ans = 0;
            std::sort(a, a + n, cmp);
            dfs(0, 0);
            printf("%d
    ", ans);
        }
        return 0;
    }

    HDU 1059

         多重背包。

    View Code
    #include <stdio.h>
    #include <string.h>
    #define N 60010
    
    int f[N], V;
    
    inline int max(int x, int y){return x > y ? x : y;}
    
    void ZeroOnePack(int c, int w)
    {
        int i;
        for(i = V; i >= c; i --)
            f[i] = max(f[i], f[i - c] + w);
    }
    
    void CompletePack(int c, int w)
    {
        int i;
        for(i = c; i <= V; i ++)
            f[i] = max(f[i], f[i - c] + w);
    }
    
    void MultiplePack(int c, int w, int n)
    {
        if(c * n >= V)
        {
            CompletePack(c, w);
            return ;
        }
        int k = 1;
        while(k <= n)
        {
            ZeroOnePack(k * c, k * w);
            n -= k;
            k *= 23;
        }
        ZeroOnePack(n * c, n * w);
    }
    
    int main()
    {
        int i, sum, n[6], cas = 1;
        while(1)
        {
            sum = 0;
            for(i = 0; i < 6; i ++)
            {
                scanf("%d", &n[i]);
                sum += (i + 1) * n[i];
            }
    
            if(sum == 0)
                break;
    
            printf("Collection #%d:
    ", cas ++);
    
            if(sum & 1)
            {
                printf("Can't be divided.
    
    ");
                continue;
            }
    
            V = sum / 2;
            memset(f, 0, sizeof(f));
            for(i = 0; i < 6; i ++)
                if(n[i]) MultiplePack(i + 1, i + 1, n[i]);
    
            if(f[V] == V)
                printf("Can be divided.
    
    ");
            else
                printf("Can't be divided.
    
    ");
        }
        return 0;
    }

    HDU 2955 Robberies

      一开始傻逼了,居然直接把概率进行相加,用转移方程f[i] = max(f[i], f[i-v[j].money] + v[j].p],这是错的。

      正确解法是:设f[i]为得到总钱数为 i 的最大逃脱概率,那么有转移方程 f[i] = max(f[i], f[i-v[j].money] * v[j].p),逃脱概率v[j].p = 1.- 被抓捕概率。那么最后结果就是大于题目给定最大逃脱概率前提下的最多钱数。

     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <algorithm>
     4 #define N 10001
     5 
     6 double f[N];
     7 
     8 int main()
     9 {
    10     int T, n, mm;
    11     double p, pp;
    12     scanf("%d", &T);
    13     while(T--)
    14     {
    15         scanf("%lf%d", &p, &n);
    16         p = 1 - p;
    17         for(int i = 0; i < N; i++)
    18             f[i] = 0.0;
    19         f[0] =  1.0;
    20         for(int i = 0; i < n; i++)
    21         {
    22             scanf("%d%lf", &mm, &pp);
    23             pp = 1.0 - pp;
    24             for(int j = N-1; j >= mm; j--)
    25                 f[j] = std::max(f[j], f[j - mm] * pp);
    26         }
    27         int i;
    28         for(i = N-1; i >= 0; i--)
    29             if(f[i] >= p)
    30                 break;
    31         printf("%d
    ", i);
    32     }
    33     return 0;
    34 }
    View Code

        努力更新中……

     

  • 相关阅读:
    【python】opencv教程CV2模块——图片处理,裁剪缩放加边框
    【python】opencv教程CV2模块——画图,来左边跟我一起画星星在右边画彩虹
    【python】opencv教程CV2模块——图片处理,剪切缩放旋转
    【python】opencv教程CV2模块——批量视频截屏
    【python】opencv教程CV2模块——视频捕获,延时摄影视频、鬼畜表情包密集制作
    代码-JS之正则验证邮箱格式
    代码-JS之正则解决结巴程序
    代码-JS之IE+GOOGLE兼容函数
    代码-JS之正则replace函数
    代码-JS之下拉菜单
  • 原文地址:https://www.cnblogs.com/huangfeihome/p/2665428.html
Copyright © 2020-2023  润新知