题目链接: E. Removal
题目大意
一个数组s,长度为n( n≤105) n ≤ 10 5 ) ,数组元素 s[i]≤10 s [ i ] ≤ 10 , 要求从中删除m( m≤10) m ≤ 10 ) 个数字,求能得到多少个不重复的结果, mod 1e9+7
思路
动态规划,如果不考虑重复,令dp[i][j] := 前i个数字删除j个后有多少种结果
,则有dp[i][j] = dp[i-1][j-1] + dp[i-1][j]
, 即前i-1个数字中删除j-1个再删除第i个数字 + 前i-1个中删除j个
但这样会有重复
考虑{…, 1, 2, 3, 1, 2}删除3个数字,{…, 1, 2, 3, 1, 2}({…, 1, 2})和{…, 1, 2, 3, 1, 2}是一样的({…, 1, 2}),也就是说删除两个相同数字之间的所有数字+这两个数字中的一个会有一次重复计数,需要减去,在例子中{…, 1, 2}被计数了两次,要减去一次(即减去{…}的次数),令pre[i] := s[i]前面第一个和s[i]相同的元素位置
, 则dp[i][j]
应该减去dp[pre[i]-1][j-(i-pre[i])]
代码
语言:C++14 代码长度:756 运行时间: 176 ms 占用内存:9436K 运行结果:答案正确
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e5+100, mod = 1e9+7;
int n, m, k, s[maxn];
int dp[maxn][20];
int pre[maxn];
int prex[20];
int main()
{
while(scanf("%d%d%d", &n, &m, &k) == 3)
{
memset(dp, 0, sizeof(int)*20*(n+1));
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)
{
dp[i][j] = dp[i-1][j] + dp[i-1][j-1];
if(pre[i] && d<=j) dp[i][j] -= dp[pre[i]-1][j-d];
dp[i][j] %= mod;
dp[i][j] += mod;
dp[i][j] %= mod;
}
}
printf("%d\n", dp[n][m]);
}
return 0;
}