这是一个计数类的dp
dp[i][j]表示前i个数字中,删除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个元素,那么重复的方案数就是
图片说明
就是说,要想有重复的序列,一定是能删除完两个相同数字之间的数,也就是i-last[i]个,那么其实就是last[i]-1前面选一部分再加上这个重复的数字
那么因为要删除j个,还需要删除j-(i-last[i])个 肯定就是从last[i]-1前面选择

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
ll dp[1 << 17][11];
ll a[1 << 17], c[1 << 17], last[1 << 17];
int main()
{
    int n, m, k;
    while (cin >> n >> m >> k){
        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;
    }
    return 0;
}