zoukankan      html  css  js  c++  java
  • 入门算法题——动态规划篇(二)

    转载请注明出处:http://www.cnblogs.com/xianyue

    题1:最长连续非降子序列

    题目描述

    给你一个长度为n个数组,求该数组的最长连续非降子序列的长度。

    输入

    第一行一个正整数n,表示数组的长度。
    第二行n个正整数,表示数组a。

    输出

    数组a的最长连续非降子序列的长度。

    样例输入

    6
    2 2 1 3 4 1

    样例输出

    3

    题解

    if a[i] >= a[i-1]: f[i] = f[i-1] + 1
    else : f[i] = 1
    然后求最大的f[i],即为答案。

    代码

    #include <iostream>
    using namespace std;
    int n, a[100100], f[100100], res = 0;
    int main()
    {
        cin >>n;
        for (int i = 0; i < n; i ++)
            cin >> a[i];
        f[0] = 1;
        for (int i = 1; i < n; i ++)
        {
            if (a[i] >= a[i-1])
                f[i] = f[i-1] + 1;
            else
                f[i] = 1;
        }
        for (int i = 0; i < n; i ++)
        {
            if (f[i] > res)
                res = f[i];
        }
        cout << res << endl;
        return 0;
    }
    

    题2:AB与BA子串

    题目描述

    给你一个字符串S,请问能否在里面找到不重叠的两个子串"AB"和"BA"?

    输入

    一个字符串S

    输出

    如果能够在S中找到一对不重叠的"AB"和"BA"子串,则输出"YES",否则输出"NO"。

    样例输入

    BACFXAB

    样例输出

    YES

    题解

    我们可以开两个数组f,g,其中:

    • f[i][0]表示S(0...i)中是否包含"AB"
    • f[i][1]表示S(0...i)中是否包含"BA"
    • g[i][0]表示S(i...n-1)中是否包含"AB"
    • g[i][1]表示S(i...n-1)中是否包含"BA"

    其中S(i...j)代表S[i]到S[j]组成的一个S的子串。
    然后正序循环一遍就能求得f,逆序循环一遍就能求得g。

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    const int maxn = 100010;
    char s[maxn];
    int f[maxn][2], g[maxn][2], n;
    int main()
    {
        cin >> s;
        n = strlen(s);
        for (int i = 1; i < n; i ++)
        {
            int t1 = (s[i-1] == 'A' && s[i] == 'B');
            f[i][0] = max(f[i-1][0], t1);
            int t2 = (s[i-1] == 'B' && s[i] == 'A');
            f[i][1] = max(f[i-1][1], t2);
        }
        for (int i = n-1; i >= 0; i --)
        {
            int t1 = (s[i] == 'A' && s[i+1] == 'B');
            g[i][0] = max(g[i+1][0], t1);
            int t2 = (s[i] == 'B' && s[i+1] == 'A');
            g[i][1] = max(g[i+1][1], t2);
        }
        bool flag = false;
        for (int i = 1; i < n-1; i ++)
            if (f[i][0] && g[i+1][1] || f[i][1] && g[i+1][0])
            {
                flag = true;
                break;
            }
        puts(flag ? "YES" : "NO");
        return 0;
    }
    

    题3:遛狗

    题目描述

    丁丁今天领养了一只狗狗——灰灰。但是他发现为了保持狗狗健康,他必须在连续的两天至少出门遛狗k千米。假设k=5,那么假设我今天遛狗遛了2千米,则我明天必须至少遛狗3千米才能保持狗狗健康。丁丁看了一下字节接下来n天的计划表,发现自己第i天需要出门a[i]千米(买零食,超市购物……)。但是他发现自己的计划表并不能满足遛狗计划,但是他又不想多跑,所以请你帮他设计一张新的计划表,b[i]>=a[i],并且能够满足遛狗计划。我们需要增加的千米数最少的一种方案。

    输入

    第一行两个正整数n,k。
    第二行n个正整数表示数组a。

    输出

    第一行一个正整数用于表示至少需要增加的千米数。
    第二行n个数字表示数组b。

    样例输入1

    3 5
    2 0 1

    样例输出1

    4
    2 3 2

    样例输入2

    4 6
    2 4 3 5

    样例输出2

    0
    2 4 3 5

    题解

    状态转移方程为:
    b[i] = max(a[i], k-b[i-1])

    代码

    #include <iostream>
    using namespace std;
    const int maxn = 550;
    int n, a[maxn], b[maxn], k, sum = 0;
    int main()
    {
        cin >> n >> k;
        for (int i = 0; i < n; i ++)
            cin >> a[i];
        b[0] = a[0];
        for (int i = 1; i < n; i ++)
        {
            b[i] = max(a[i], k-b[i-1]);
            sum += b[i] - a[i];
        }
        cout << sum << endl;
        for (int i = 0; i < n; i ++)
            cout << b[i] << " ";
        return 0;
    }
    

    题4:不同的后缀

    题目描述

    给你一个长度为n的数组a(坐标从1开始),然后接下来有m次询问,每一次询问都给出一个数i,你需要回答出a[i]到a[n]中间有多少个不同的数。

    输入

    第一行两个正整数n,m。
    第二行n个数表示数组a。
    然后m行,每一行一个正整数i(1<=i<=n)。

    输出

    针对每一个询问i给出a[i]到a[n]之间有多少个不同的数。

    样例输入

    10 10
    1 2 3 4 1 2 3 4 100000 99999
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    样例输出

    6
    6
    6
    6
    6
    5
    4
    3
    2
    1

    题解

    开一个数组f,f[i]表示a[i]到a[n]之间有多少个不同的数。
    状态转移方程为:

    f[i] = f[i+1] + (a[i]有没有出现过?1:0)
    

    不过在实现的时候我是开了一个变量cnt记录的。

    代码

    #include <iostream>
    #include <map>
    using namespace std;
    const int maxn = 100010;
    map<int, int> mp;
    int n, m, a[maxn], f[maxn], l, cnt;
    int main()
    {
        cin >> n >> m;
        for (int i = 1; i <= n; i ++)
            cin >> a[i];
        cnt = 0;
        for (int i = n; i >= 1; i --)
        {
            if (!mp[ a[i] ])
            {
                mp[ a[i] ] = 1;
                cnt ++;
            }
            f[i] = cnt;
        }
        while (m--)
        {
            cin >> l;
            cout << f[l] << endl;
        }
        return 0;
    }
    

    题5: 三分数组

    题目描述

    给你一个长度为n的数组a,在其中找i,j(1<=i<=j<n),满足:

    求方案数。

    输入

    第一行一个正整数n。
    第二行n个数表示数组a。

    输出

    输出有多少种方案

    样例输入

    5
    1 2 3 0 3

    样例输出

    2

    题解

    这里假设两个分隔的数。
    首先计算一下数组a的总和sum。
    s[i]表示a[1]到a[i]的总和
    然后开一个数组f1,f1[i]表示a[1]到a[i]一共有多少个数值为sum/3的。
    f2[i]表示以a[i]为第二个分割的数的方案数。
    则:
    f1[i] = f1[i-1] + (s[i] == sum/3)
    f2[i] = f2[i-1] + f1[i-1] * (s[i] == sum*2/3)

    代码

    #include <iostream>
    using namespace std;
    const int maxn = 500050;
    int n, a[maxn];
    long long sum = 0, s[maxn], f1[maxn], f2[maxn];
    int main()
    {
        cin >> n;
        for (int i = 0; i < n; i ++)
            cin >> a[i];
        for (int i = 0; i < n; i ++)
            sum += a[i];
        s[0] = a[0];
        for (int i = 1; i < n; i ++)
            s[i] = s[i-1] + a[i];
        if (sum % 3 != 0)
            cout << "0" << endl;
        else
        {
            f1[0] = (a[0] == sum/3);
            for (int i = 1; i < n - 1; i ++)
            {
                f1[i] = f1[i-1] + (s[i] == sum/3);
                f2[i] = f2[i-1] + f1[i-1] * (s[i] == sum*2/3);
            }
            cout << f2[n-2] << endl;
        }
        return 0;
    }
    
  • 相关阅读:
    mouse_event,keybd_event
    构造自己的StringGrid
    BMP2JPG,JPG2BMP
    $J的用法
    一个简单的系统托盘程序
    构造自己的双向链表
    C++的iostream标准库介绍
    D3DXVec4Dot 函数
    3D游戏从头编之D3D(3)
    D3DXVec4Cross 函数
  • 原文地址:https://www.cnblogs.com/xianyue/p/7141006.html
Copyright © 2011-2022 走看看