F - Halloween Costumes 区间dp

题目大意:

一个人去参加晚会,不同的晚会要穿不同的衣服,他一次性可以穿几件衣服,但是衣服一旦脱下来就变成旧衣服了,他就不会再穿了,问他参加n个晚会,至少要穿几件衣服。

题解:

这个题目可以转化成涂色问题,每一次可以连续涂若干长度的颜色,问最少涂多少次颜色,使得这个序列变成我们想要的颜色。

\(dp[i][j]\) 表示从 \(i\)\(j\) 最少涂 \(dp[i][j]\) 次颜色。

这个转移方程怎么写呢?可以以左端点作为判断的依据。

对于一个区间 \([l,r]\) 假设最先从左端点开始涂颜色,如果这个区间还有和左端点的颜色是一样的,那么则说明这个点就是最开始已经涂过了,所以以这个点为分节符,从这个点的两边转移过来。

\(dp[i][j]=min(dp[i][j],dp[i][k-1]+dp[k+1][j])\)

#include <bits/stdc++.h>
#define id first
#define val second
#define inf 0x3f3f3f3f
#define inf64 0x3f3f3f3f3f3f3f3f
using namespace std;
const int maxn=110;
typedef long long ll;
int dp[maxn][maxn],a[maxn];

int main(){
    int t;
    scanf("%d",&t);
    for(int cas=1;cas<=t;cas++){
        int n;
        scanf("%d",&n);
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++) scanf("%d",&a[i]),dp[i][i]=1;
        for(int len=2;len<=n;len++){
            for(int i=1;i+len-1<=n;i++){
                int j=i+len-1;
                dp[i][j]=dp[i+1][j]+1;
                for(int k=i+1;k<=j;k++){
                    if(a[k]==a[i]) dp[i][j]=min(dp[i][j],dp[i][k-1]+dp[k+1][j]);
                }
            }
        }
        printf("Case %d: %d\n",cas,dp[1][n]);
    }
    return 0;
}