• 【每日一题】23.Removal (计数DP)


    补题链接:Here

    计数DP讲解:Here

    这是一个计数类的dp
    dp[i][j]表示前i个数字中,删除j个元素的方案数

    很容易得到转移方程:(f[i][j] = f[i - 1][j - 1] + dp[i - 1][j])

    意思就是前i个删除j个,要么从前i-1个中删除了j-1个,等于第i个也要删,要么从前i-1个删除了j个,等于第i个不删

    这样还没做完,下面考虑去除重复的个数。(其实这就是个容斥的思想,先尽管算,再把重复的去除就是了)
    考虑一下怎么会出现重复的情况。
    ……1,4,7,8,5,1

    很容易发现,对于1,4,7,8,5,1这一段,删除1,4,7,8,5 和删除4,7,8,5,1 会造成重复计算。

    重复的方案数是多少?其实就是第一个1前面的序列随便选加上后面的1 4 7 8 5 1后再删除1 4 7 8 5 或者4 7 8 5 1
    假如是要删除j个元素,那么重复的方案数就是 (dp[last[i] - 1][j - (i - last[i])])

    其中 (last[i]) 表示前面与当前 (a[i]) 相同数字的最近位置

    就是说,要想有重复的序列,一定是能删除完两个相同数字之间的数,也就是i-last[i]个,那么其实就是last[i]-1前面选一部分再加上这个重复的数字
    那么因为要删除j个,还需要删除j-(i-last[i])个 肯定就是从last[i]-1前面选择

    // RioTian 21/05/11
    #include <bits/stdc++.h>
    using namespace std;
    using ll     = long long;
    const ll mod = 1e9 + 7;
    int n, m, k;
    ll dp[1 << 17][11];
    ll a[1 << 17], c[1 << 17], last[1 << 17];
    void solve() {
        memset(last, 0, sizeof last);
        memset(c, 0, sizeof c);
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            last[i] = c[a[i]]; //记录前面与当前数字相同的最近位置
            c[a[i]] = i;       //更新a[i]的新位置
        }
        for (int i = 0; i <= n; i++) dp[i][i] = dp[i][0] = 1; //初始化前i个删除i个,前i个删除0个的方案数
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= min(i - 1, m); j++) {
                dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j]) % mod;                         //转移
                if (last[i] != 0 && i - last[i] <= j) {                                     //如果前面有与a[i]相同的数字 并且之间的数字不够删除
                    dp[i][j] = (dp[i][j] - dp[last[i] - 1][j - (i - last[i])] + mod) % mod; //去重
                }
            }
        }
        cout << dp[n][m] << endl;
    }
    int main() {
        ios_base::sync_with_stdio(false), cin.tie(0), cout.tie(0);
        while (cin >> n >> m >> k) solve();
        return 0;
    }
    

    The desire of his soul is the prophecy of his fate
    你灵魂的欲望,是你命运的先知。

  • 相关阅读:
    Codeforces Round #603 (Div. 2) E. Editor(线段树)
    Codeforces Round #603 (Div. 2) D. Secret Passwords(并查集)
    Java的DAO设计模式
    js实现本地时间同步
    循环播放
    正则表达式(2)
    正则表达式(1)
    第十八个知识点:画一个描述ECB,CBC,CTR模式的操作
    第十七个知识点:描述和比较DES和AES的轮结构
    第四十一个知识点 所有的侧信道分析都是能量分析吗
  • 原文地址:https://www.cnblogs.com/RioTian/p/14755319.html
Copyright © 2020-2023  润新知