1.题目描述
题目描述
Bobo has a sequence of integers s1, s2, …, sn where 1 ≤ si ≤ k.
Find out the number of distinct sequences modulo (109+7) after removing exactly m elements.
输入描述:
The input consists of several test cases and is terminated by end-of-file.
The first line of each test case contains three integers n, m and k.
The second line contains n integers s1, s2, …, sn.
输出描述:
For each test case, print an integer which denotes the result.
示例:
输入
3 2 2
1 2 1
4 2 2
1 2 1 2
输出
2
4
备注:
- 1 ≤ n ≤ 1e5
- 1 ≤ m ≤ min{n - 1, 10}
- 1 ≤ k ≤ 10
- 1 ≤ si ≤ k
- The sum of n does not exceed 1e6.
2.AC代码
先贴代码:
#include <bits/stdc++.h>
using namespace std;
#define fer(i,a,b) for(int i = a ; i <= b ; ++ i)
#define der(i,a,b) for(int i = a ; i >= b ; -- i)
#define all(x) (x).begin(),(x).end()
#define de(x) cout << x << "\n"
#define sf(x) scanf("%lld",&x)
#define pll pair<int,int>
//#define re register int
#define int long long
#define pb push_back
#define y second
#define x first
const int N=1e5+10;
const int mod=1e9+7;
int a[N];
int dp[N][15];
signed main()
{
int n,m,k;
while(~scanf("%lld%lld%lld",&n,&m,&k))
{
memset(dp,0,sizeof(dp));
fer(i,0,n)
dp[i][0]=1;
fer(i,1,n)
sf(a[i]);
fer(i,1,n)
{
fer(j,1,m)
{
int pos=0;
dp[i][j]=(dp[i-1][j]+dp[i-1][j-1])%mod;
der(kk,i-1,1)
{
if(a[i]==a[kk])
{
pos=kk;
break;
}
}
if(i-pos<=j&&pos>=1) dp[i][j]=(dp[i][j]-dp[pos-1][j-(i-pos)]+mod)%mod;
}
}
de(dp[n][m]);
//printf("%lld\n",dp[n][m]%mod);
}
return 0;
}
3.个人看法
dp题写的不熟,第一次写这道题时没写出来。参考了一些大佬的题解,算是弄懂了里面的玄机。我们把dp[i][j]当做从前i个元素里删除j个元素后的不同序列的个数,那么答案就是dp[n][m]。然后我们仔细思考就可以得到这样的表达式:dp[i][j]=dp[i-1][j]+dp[i-1][j-1],然而我们再仔细思考就会发现我们的方程遗漏了重复元素,还要再减去重复元素的个数。比如,1,2,4,5,6,7,4,我们删除4,5,6,7和删除5,6,7,4后的序列都是1,2,4,就有了重复序列,我们要减去这个序列的个数。在这里就要开动你的脑筋了,想想这些产生这些重复序列的共同点了,共同点就是,两个相同元素之间的所有元素都会被删除,所以产生重复序列的前提条件就是j>=i-pos,还有就是要有相同元素,保证pos>=1就行,j为要删除元素的个数,pos为前面第一个与a[i]等值的下标,故要减掉dp[pos-1][j-(i-pos)].在实际的代码书写当中,我们每次都只选取前面第一个,这时,可能就有人提出疑问了,相同元素如果有三个以上时该怎么办呢?只选前面第一个元素的下标,那第二个第三个怎么办呢?这是个好问题,我们举个例子吧。1,2,4,5,6,4,2,4,我们就选取第一个4和第三个4举例吧,按照前面所讲,两个4之间的元素都删除,序列就为1,2,4,你会惊人的发现这个序列就是第一个4和第二个4删除之后的序列!所以,这里我们就不需要考虑重复元素了,前面已经帮我们删除了,我们直接用就是。
总的来说这道dp题的设计很巧妙,除了要得出一般的状态转移方程外,还要减去重复元素的序列,这对做题人的思维缜密有比较高的要求,而且在实际写代码的过程中,对多个重复元素的处理方法的巧妙也是让人叹为观止,这就是长期刷题才培养出来的做题思维啊!
4.原题链接
https://ac.nowcoder.com/acm/contest/20322/E