P2072 宗教问题

· · 题解

这是一道妥妥的动态规划。

出题人也是真牛,出了一道二合一的题。

好了,步入正题。

思路

我们先来看第一小问:这 N 个教徒至少要分为几个集体。

我们很容易想到用 f_i 表示前 i 个人,至少要分成多少组。

对于每个 f_i,枚举它的断点,然后加上 ji 的贡献。

再来看第二小问:这些集体的危险值总和至少为多少。 - 状态设计 同样的,我们用 $dp_i$ 表示前 $i$ 个人,种类数和的最小值。 - 动态转移方程 也是枚举断点。 $dp_i=\min(dp_i,dp_{j-1}+sum_{j,i})$。 $sum_{j,i}$ 表示从 $j$ 到 $i$ 的种类数。 直接枚举会超时,我们就倒序枚举,每次加上种类数,在枚举时统计。 再二合一,得到[代码](https://www.luogu.com.cn/record/143440552)。 # 代码 ```cpp #include<bits/stdc++.h> using namespace std; const int N=1010,M=30,inf=0x3f3f3f3f; int n,m,k,i,j,f[N],dp[N],cnt[M],a[N],sum; int main() { scanf("%d%d%d",&n,&m,&k); for (i=1;i<=n;i++) scanf("%d",&a[i]); for (i=1;i<=n;i++) { f[i]=dp[i]=inf; memset(cnt,0,sizeof(cnt)); sum=0; for (j=i;j>=1;j--) { if (!cnt[a[j]]) { cnt[a[j]]++; sum++; } if (sum>k) break; f[i]=min(f[i],f[j-1]+1); dp[i]=min(dp[i],dp[j-1]+sum); } } printf("%d\n%d",f[n],dp[n]); return 0; } ```