div.3真是优秀dp层出的场次.
题意
给一个字符串,令一个子序列的价值为原字符串变成该子序列去掉字母的个数,求选择 k k k个各不相同的子序列所得价值的最小值.不能选择 k k k个各不相同的子序列则输出 − 1 -1 −1.
题解
显然从贪心考虑我们应当尽量选择较长的子序列.这给我们一个
d
p
dp
dp思路即为对每一个
i
∈
[
1
,
n
]
i\in[1,n]
i∈[1,n]求得长度为
i
i
i各不相同子序列的个数.
令
d
p
i
,
j
dp_{i,j}
dpi,j表示前
i
i
i个字母中长度为
j
j
j的各不相同的子序列个数.
倘若字母没有重复,则答案即为
C
i
j
C_i^j
Cij,可以运用杨辉三角进行处理,
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
j
−
1
]
+
d
p
[
i
−
1
]
[
j
]
dp[i][j]=dp[i-1][j-1]+dp[i-1][j]
dp[i][j]=dp[i−1][j−1]+dp[i−1][j].
但现在有重复字母,那么假设第
i
i
i位的字符上次出现的位置为
p
r
e
i
pre_i
prei,则还需要减掉重复出现的部分
d
p
[
p
r
e
[
i
]
−
1
]
[
j
−
1
]
dp[pre[i]-1][j-1]
dp[pre[i]−1][j−1].
这样
d
p
n
,
i
dp_{n,i}
dpn,i即为长度为
i
i
i子序列的个数.
那么从
i
=
n
i=n
i=n开始贪心取
k
k
k个子序列即可.
const int yuzu=2e5;
typedef int fuko[yuzu|10];
ll dp[150][150]={1},pre[150];
char c[150];
int main() {
ll n,k,i,j,zw=0;
read(n),read(k);
scanf("%s",c+1);
for (i=1;i<=n;++i) {
for (j=0;j<=i;++j) {
dp[i][j]=j?dp[i-1][j-1]+dp[i-1][j]:1;
if (j&&pre[c[i]]) dp[i][j]-=dp[pre[c[i]]-1][j-1];
}
pre[c[i]]=i;
}
for (i=n;~i&&k;--i) zw+=min(dp[n][i],k)*(n-i),k-=min(dp[n][i],k);
printf("%lld\n",k?-1:zw);
}