动态规划题目复习(acwing59把数字翻译成字符串,acwing 80. 骰子的点数,AcWing 30. 正则表达式匹配,一道还不会做的笔试题)

线性动态规划复习


顺序式递推
AcWing 59. 把数字翻译成字符串
给定一个数字,我们按照如下规则把它翻译为字符串:
0 翻译成 a,
1 翻译成 b
,……,
11 翻译成 l,
……,25
翻译成 z。
一个数字可能有多个翻译。
例如 12258 有 5种不同的翻译,它们分别是 bccfi、bwfi、bczi、mcfi 和 mzi。
请编程实现一个函数用来计算一个数字有多少种不同的翻译方法。

数据范围
输入数字位数 [1,101]。

样例
输入:“12258”

输出:5

思路:使用dp[i]表示子字符串[0,i]的翻译方法数,则dp[i]=dp[i-1]+dpi-2
代码:

class Solution {
public:
    const static int maxn = 105;
    int dp[maxn];
    int getTranslationCount(string s) {
        dp[1] = 1;
        dp[0] = 1;
        int n = s.length();
        for(int i = 2; i <= n; i++) {
            dp[i] = dp[i - 1];
            if(s[i-2] != '0' && stoi(s.substr(i-2,2)) < 26) {
                dp[i] += dp[i-2];
            }
        }
        
        return dp[n];
    }
};

acwing30. 正则表达式匹配
请实现一个函数用来匹配包括’.‘和’*'的正则表达式。

模式中的字符’.‘表示任意一个字符,而’*'表示它前面的字符可以出现任意次(含0次)。

在本题中,匹配是指字符串的所有字符匹配整个模式。

例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab*a"均不匹配。

数据范围
输入字符串长度 [0,300]

样例
输入:

s=“aa”
p=“a*”

输出:true

思路:用dp[i][j]表示i开头的带匹配串和j开头的模式串是否匹配,遍历模式串,如果模式串当前字符是字母或者’.',则dp[i][j]=dp[i-1][j-1]+(字符是否匹配),如果当前字符是*,则可分两种情况,一种是不使用*前面的字符dp[i][j]=dp[i][j-2]+(字符是否匹配),一种是使用*前面的字符,dp[i][j] |= dp[i-1][j-2]

代码:

class Solution {
public:
    const static int maxn = 305;
    int dp[maxn][maxn];
    bool isMatch(string s, string p) {
        dp[0][0] = 1;
        int n = s.length(), m = p.length();
        for(int i = 0; i <= n; i++) {
            for(int j = 1; j <= m; j++) {
                if(p[j - 1] == '*') {
                    dp[i][j] |= dp[i][j-2];
                    if(i >= 1 && j > 1 && (s[i - 1] == p[j - 2] || p[j - 2] == '.')) 
                    {
                        dp[i][j] |= dp[i-1][j];
                    }
                }
                else if(i > 0){
                    dp[i][j] = dp[i-1][j-1] && (s[i-1] == p[j-1] || p[j-1] == '.');
                }
            }
        }
        return dp[n][m];
    }
};

分段式递推
acwing 80. 骰子的点数
将一个骰子投掷 n次,获得的总点数为 s,s的可能范围为 n∼6n。
掷出某一点数,可能有多种掷法,例如投掷 2次,掷出 3点,共有 [1,2],[2,1]两种掷法。

请求出投掷 n次,掷出 n∼6n点分别有多少种掷法。

数据范围
1≤n≤10
样例1
输入:n=1

输出:[1, 1, 1, 1, 1, 1]

解释:投掷1次,可能出现的点数为1-6,共计6种。每种点数都只有1种掷法。所以输出[1, 1, 1, 1, 1, 1]。
样例2
输入:n=2

输出:[1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]

解释:投掷2次,可能出现的点数为2-12,共计11种。每种点数可能掷法数目分别为1,2,3,4,5,6,5,4,3,2,1。

所以输出[1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]。

思路:令dp[i][k]表示通过k次摇骰子获得数字i的情况数,则dp[i][k] = sum(dp[i-j][k-1]), sum表示求和,j~[1,6]

代码:

class Solution {
public:
    const static int maxn = 100;
    int dp[maxn][maxn];
    vector<int> numberOfDice(int n) {
        int m = n * 6;
        vector <int> ans;
        dp[0][0] = 1;
        for(int i = 1; i <= m; i++) {
            for(int k = 1; k <= n; k++) {
                for(int j = 1; j <= 6; j++) {
                    if(i - j >= 0) {
                        dp[i][k] += dp[i - j][k - 1];
                    }
                }
            }
            
            
        }
        for(int i = n; i <= m; i++) {
            ans.push_back(dp[i][n]);
        }
        return ans;
    }
};

分割序列:
题面:定义f(A)表示将序列A进行unique操作之后的序列的元素个数。unique操作是指将相邻目相同的元素合成一个元素,再按照原序列的相对顺序进行排列之后得到的序列。例如[1 1 1 2 2 3 3 1]进行unique操作之后的序列为[1 2 3 1] ;[1 2 3 3 2 1 1]进行unique操作之后的序列为[1,2,3,2,1];[1111]进行unique 操作之后得到的序列为1。现在,输入一个长度为n的序列S,你需要将其划分为k段,使得每一段都不为空,且你需要最大化所有段的f函数的值之和。你只需要输出这个最大值就行

样例输入:

8 3
1 1 1 2 2 3 3 1

样例输出:

6

思路:(没找到正解,打暴力版递推式)dp[i][j]=max(dp[l][j-1] + f(i:j), dp[l][j-1])

代码:(暂时没想到正解,目前复杂度O(nnk*log(m), m为数组中不同元素的个数)

#include 
#include 
#include 
#include 

using namespace std;

const int maxn = 1e5 + 5;
const int N = 1e3 + 5;


int a[maxn];
int dp[maxn][N];
map <int, int> mp;


int main() {
    int n, k;
    cin >> n >> k;
    for(int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    dp[1][1] = 1;
    
    for(int i = 1; i <= n; i++) {
        for(int l = 1; l <= k; l++) {
            mp.clear();
            int cnt = 0;
            for(int j = i; j >= 1; j--) {
                if(!mp.count(a[j]))cnt++;
                mp[a[j]]=1;
                dp[i][l] = max(dp[i][l], dp[j][l - 1] + cnt);
            }
        }
    }
    cout << dp[n][k] << endl;
    return 0;
}

你可能感兴趣的:(动态规划)