入门算法题——动态规划篇(二)

转载请注明出处: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;
}
原文地址:https://www.cnblogs.com/xianyue/p/7141006.html