题解 CF607B 【Zuma】

QwQcOrZ

2019-07-25 15:08:18

Solution

区间dp 其它的题解做法比较玄学 ~~至少我是没有看懂~~,或者是用递归实现转移的 ~~于是我决定发篇题解~~ ------------ 题意:从一个序列中每次取出一个回文串,求最少取几次(取出后两端外的数会相接) ------------ 设$dp[i][j]$为在$[i,j]$的闭区间内最少的花费 显然 $\begin{cases}dp[i][i]=1\\dp[i][i+1]=1\quad (a[i]=a[i+1])\\dp[i][i+1]=2\quad (a[i]\ne a[i+1])\end{cases}$ ~~原因请读者自行脑补~~ 于是可以推出转移方程: $\begin{cases}dp[i][j]=dp[i+1][j-1]\quad (a[i]=a[j])\\\\dp[i][j]=min_{k=i}^{j-1}dp[i][k]+dp[k+1][j]\end{cases}$ 然后就可以愉快的dp辣 ```cpp #include<bits/stdc++.h> using namespace std; const int N=505; const int inf=233333333; int n,a[N],dp[N][N]; int main() { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) dp[i][j]=inf; //开始全赋inf,否则转移取min时就会挂掉 for (int i=1;i<=n;i++) dp[i][i]=1; //直接取一个数,花费一个代价 for (int i=1;i<n;i++) dp[i][i+1]=1+(a[i]!=a[i+1]); //取两个数的情况,如果相等取一次就好了,否则取两次 for (int i=3;i<=n;i++) //注意转移时需要先枚举区间长度 for (int j=1;i+j-1<=n;j++) { int l=j,r=i+j-1; //计算区间左右端点 if (a[l]==a[r]) dp[l][r]=dp[l+1][r-1]; for (int k=l;k<r;k++) dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]); //转移 } printf("%d",dp[1][n]); return 0; } ```