动态规划 - 线性DP

数字三角形

const int N = 510, INF = 0x3f3f3f3f;
int n, m[N][N], dp[N][N];
int main()
{
    // ------- 0 based index --------
    cin >> n;
    for(int i = 0; i < n; ++i)
        for(int j = 0; j <= i; ++j)
            cin >> m[i][j];
    // fill(dp[0], dp[0] + N * N, -INF);    // 避免一些不相关位置的初始值干扰状态更新
    for(int i = 0; i < N; ++i)
        for(int j = 0; j < N; ++j)
            dp[i][j] = -INF;
    dp[0][0] = m[0][0];
    for(int i = 1; i < n; ++i)
    {
        for(int j = 0; j <= i; ++j)
        {
            if(j == 0)  					// 因为是 0 based index,所以左边界要手动判断                    
                dp[i][j] = dp[i - 1][j] + m[i][j];
            // else if(j == i)              // 若没有初始化-INF避免干扰,则需手动判断边界
            //     dp[i][j] = dp[i - 1][j - 1] + m[i][j];
            else
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1]) + m[i][j];
        }
    }
    // Test code
    // for(int i = 0; i < n; ++i)
    // {
    //     for(int j = 0; j <= i; ++j)
    //         cout << dp[i][j] << " ";
    //     cout << endl;
    // }
    
    int ret = -0x3f3f3f3f;
    for(int j = 0; j < n; ++j)
        ret = max(ret, dp[n - 1][j]);
        
    cout << ret;
    
    
    // ------- 1 based index --------
    // cin >> n;
    // for(int i = 1; i <= n; ++i)
    //     for(int j = 1; j <= i; ++j)
    //         cin >> m[i][j];
    // // fill(dp[0], dp[0] + N * N, -INF);    // 避免一些不相关位置的初始值干扰状态更新
    // for(int i = 0; i < N; ++i)
    //     for(int j = 0; j < N; ++j)
    //         dp[i][j] = -INF;
    // dp[1][1] = m[1][1];
    // for(int i = 2; i <= n; ++i)
    // {
    //     for(int j = 1; j <= i; ++j)
    //     {
    //         // if(j == 0)                      // 若没有初始化避免干扰,则需手动判断边界
    //         //     dp[i][j] = dp[i - 1][j] + m[i][j];
    //         // else if(j == i)
    //         //     dp[i][j] = dp[i - 1][j - 1] + m[i][j];
    //         // else
    //             dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1]) + m[i][j];
    //     }
    // }
    
    // // for(int i = 1; i <= n; ++i)
    // // {
    // //     for(int j = 1; j <= i; ++j)
    // //         cout << dp[i][j] << " ";
    // //     cout << endl;
    // // }
    
    // int ret = -0x3f3f3f3f;
    // for(int j = 1; j <= n; ++j)
    //     ret = max(ret, dp[n][j]);
        
    // cout << ret;
    
    return 0;
}

最长上升子序列

const int N = 1010, INF = 0x3f3f3f3f;
int n, a[N], dp[N];
int main()
{
    cin >> n;
    for(int i = 0; i < n; ++i)
        cin >> a[i];
    fill(dp, dp + N, 1);
    int ret = 1;
    for(int i = 1; i < n; ++i)
    {
        for(int j = 0; j < i; ++j)
        {
            if(a[i] > a[j])
                dp[i] = max(dp[i], dp[j] + 1);
        }
        ret = max(ret, dp[i]);
    }
    // for(int i = 0; i < n; ++i)
    //     cout << dp[i] << " ";
    // cout << endl;
    cout << ret;
    return 0;
}

最长公共子序列

  • dp[i][j]:A中前 i 位的所有子序列(含i或不含i),B中前 j 位的所有子序列(含j或不含j),中的最长公共子序列的长度
  • 根据 i j 位置的情乱进行讨论
  • 针对不同情况中的重复部分的处理,这里是取最大值所以不影响
const int N = 1010;
int n, m, dp[N][N];
string s1, s2;

int main()
{
    cin >> n >> m;
    cin >> s1 >> s2;
    
    s1 = " " + s1;
    s2 = " " + s2;
    
    for(int i = 1; i <= n; ++i)
    {
        for(int j = 1; j <= m; ++j)
        {
            if(s1[i] == s2[j])
                dp[i][j] = dp[i - 1][j - 1] + 1;
            else
                dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
        }
    }
    
    cout << dp[n][m];
    return 0;
}

最短编辑距离

  • dp[i][j]:将A中1-i 改为B中1-j 的所有方法中,编辑最少的次数
  • 考虑最后一个位置,即第 i 位置 和 第 j 位置
  • 初始化: dp[i][0] = dp[0][i] = i;
const int N = 1010;
int n, m, dp[N][N];
string s1, s2;

int main()
{
    cin >> n >> s1;
    cin >> m >> s2;
    s1 = " " + s1;
    s2 = " " + s2;
    
    for(int i = 1; i < N; ++i)
        dp[i][0] = dp[0][i] = i;
        
    for(int i = 1; i <= n; ++i)
    {
        for(int j = 1; j <= m; ++j)
        {
            dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + 1;
            if(s1[i] != s2[j])
                dp[i][j] = min(dp[i][j], dp[i - 1][j -1] + 1);
            else
                dp[i][j] = min(dp[i][j], dp[i - 1][j - 1]);
        }
    }
    
    cout << dp[n][m];
    return 0;
}

编辑距离

const int N = 1010, LEN = 15;
int n, m, dp[LEN][LEN];
string s[N];
int edit_step(string s1, string s2)
{
    int n1 = s1.size(), n2 = s2.size();
    // cout << s1 << " " << s2 << endl;
    for(int i = 1; i <= n1; ++i)
        dp[i][0] = i;
    for(int i = 1; i <= n2; ++i)
        dp[0][i] = i;
    
    for(int i = 1; i <= n1; ++i)
    {
        for(int j = 1; j <= n2; ++j)
        {
            dp[i][j] = min(dp[i - 1][j], dp[i][j -1]) + 1;
            if(s1[i - 1] != s2[j - 1])
                dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + 1);
            else
                dp[i][j] = min(dp[i][j], dp[i - 1][j - 1]);
        }
    }
    return dp[n1][n2];
}

int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; ++i)
        cin >> s[i];
    
    while(m--)
    {
        string tmp;
        int bar;
        cin >> tmp >> bar;
        int cnt = 0;
        for(int i = 1; i <= n; ++i)
            if(edit_step(tmp, s[i]) <= bar)
                cnt++;
        cout << cnt << endl;
    }
        
    return 0;
}
  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值