[CSP-S 2024] 染色 题解
题目链接
题解
这是一道线性 \(dp\) 问题,难点在于在具体的题目背景中抽象出实际问题,最难的地方是分类讨论。
根据题目的意思,如果第 \(i\) 位数字(\(a_{i}\))的颜色和第 \(i\) 位之前的数字(\(a_{[1,i]}\))的颜色都不同,则这个数字贡献为 \(0\),接着,如果前面有相同的颜色,再如果离第 \(i\) 位最近的相同颜色的数字和第 \(i\) 数字相同,则第 \(i\) 位数字贡献为 \(a_{i}\)。我们只讨论这个数可以有贡献时的情况,当这个数无论如何也无法贡献时不进行讨论,我们用结构体存 \(a_{i}\) 这个数字在之前出现过的位置的下标以及这个数字本身:
struct num{ ll n,last; }a[2000005];
当 \(a_{i}\) 在之前没有出现过时,后文用 \(a_{i}(last)\) 表示 \(a_{i}\) 这个数字在之前出现过的位置的下标,\(a_{i}(last)=0\) ,接下来讨论以下情况
- 两个相同的数相邻(\(a_{i}=a_{i-1}\)),这样的两个数,只要颜色相同,无论如何都会产生贡献,但是对后续有影响(请先看下文)
- 两个相同数不相邻,存在 \(i>j\),\(a_{i}=a_{j}\) ,那么要想让 \(a_{i}\) 产生贡献,\(a_{[i+1,j-1]}\) 区间内的数字必须是同一颜色且和 \(a_{i} \ ,\ a_{j}\) 的颜色不同,那么对于 \(a_{[i+1,j-1]}\) 内的数字,如果存在两个相同的数,但这两个数又不相邻,它们的贡献就没有了。
- 两组相同的数交叉,如果想让两组数都产生贡献,必须满足下列状态:
x,...,y,x,...,y
如果是下面这种状态,只能使其中一组产生贡献:
x,...,y,...,x,...,y
经过上面的分类讨论后,我们设 \(f_{i,j=0/1}\) 表示对于前 \(i\) 个数字,当 \(j=0\),表示第 \(i\) 个数字与第 \(a_{i}(last)\) 不产生贡献时的最优解,当 \(j=1\) 时,表示第 \(i\) 个数字与第 \(a_{i}(last)\) 产生贡献时的最优解。如果两个数字既相邻又相同,要把他们的贡献算进最后的答案里,但是在表示状态时,必须表示成没有产生贡献的状态。如果出现交叉情况,直接访问 \(a_{i}(last)\) 和 \(a_{i}(last)+1\) 两个位置上的数值即可。易得到状态转移方程式:
- 普遍情况:
\( f_{i,0}=\max (f_{i-1,1}\ ,\ f_{i-1,0}) \)
\( f_{i,1}=\max (f_{a_{i}(last),1}\ ,\ f_{{a_{i}(last)},0} \ ,\ f_{a_{i}(last)+1,1}\ ,\ f_{{a_{i}(last)+1},0})+a_{i} \) - 出现相邻两数的情况时:
\( f_{i,0}=\max (f_{i-1,1}\ ,\ f_{i-1,0}) \)
\( f_{i,1}=f_{i,0} \)
将所有出现过的相邻相同两数加在一起,记为 \(anstmp\) ,最终答案就是 \(\max(f_{n,0} \ ,\ f_{n,1}) \ + \ anstmp\)
CODE
#include<cstdio> #include<algorithm> #include<cstring> #define ll long long using namespace std; ll t,anstmp; ll dp[2000005][2]; ll lst[2000005]; struct num{ ll n,last; }a[2000005]; int main(){ ll t; scanf("%lld",&t); while (t--){ ll n; scanf("%lld",&n); memset(dp,0,sizeof(dp)); memset(lst,0,sizeof(lst)); anstmp=0; for (ll i=1;i<=n;i++){ scanf("%lld",&a[i].n); if (lst[a[i].n]) a[i].last=lst[a[i].n]; else a[i].last=0; lst[a[i].n]=i; if (a[i-1].n==a[i].n) anstmp=anstmp+a[i].n; } for (ll i=1;i<=n;i++){ if (a[i].n==a[i-1].n){ dp[i][0]=max(dp[i-1][0],dp[i-1][1]); dp[i][1]=dp[i-1][1]; continue; } else dp[i][0]=max(dp[i-1][0],dp[i-1][1]); if (a[i].last) dp[i][1]=max(max(dp[a[i].last][0],dp[a[i].last][1]),max(dp[a[i].last+1][0],dp[a[i].last+1][1]))+a[i].n; } printf("%lld\n",max(dp[n][0],dp[n][1])+anstmp); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步