E | Removal |
题目:一个数组序列,有n个数,计算删掉m个数字后不相同的序列的个数;
思路:计数dp,总之就是动态规划,我们先算不考虑重复的情况就是
状态dp[ i ][ j ]:前 i 个数,删除 j 个的方法数;
状态转移方程:dp[ i ][ j ] = dp[ i - 1][ j - 1] + dp[ i - 1 ][ j ];//当前位置数字是否删除!
考虑重复(如下图):
图1 图2
从上图可以看出,重复的位置就是两个相同数字之间,那么减去就可以啦,用pre[i]记录前一个相同的数字出现的位置,然后减去这之间的数字,就是dp[ i ][ j ] - dp[ pre[i] - 1][ j - ( i - pre[i])];
代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MOD = 1e9 + 7;
const int maxn = 1e5+7;
void update(int& a, int b)
{
a += b;
if(a > MOD) a -= MOD;
}
int s[maxn], dp[maxn][20], pre[maxn], prex[20];
int main()
{
int n, m, k;
while (~scanf("%d%d%d", &n, &m, &k)) {
memset(dp, 0, sizeof(dp));
memset(prex, 0, sizeof(prex));
for(int i = 1; i <= n; i++){
scanf("%d", &s[i]);
pre[i] = prex[s[i]];
prex[s[i]] = i;
}
dp[0][0] = 1;
for(int i = 1; i <= n; i++)
{
dp[i][0] = 1;
int d = i - pre[i];
for(int j = 1; j <= m&&j <= i; j++)
{
update(dp[i][j], dp[i-1][j-1] + dp[i-1][j]);
if(pre[i]&&d<=j) update(dp[i][j] ,MOD - dp[pre[i] - 1][j - d]);
}
}
printf("%d\n",dp[n][m]);
}
}