区间dp

没有好好地做过区间dp

今天算是正式地初体验
没想出来,但是有点感觉
先暂时不说dp地推导

我直接说怎么做地吧
直接设dp[i][j]表示区间(i,j)的最小需要的衣服数量
然后直接枚举长度,之后枚举断点,dp[i][j]=min(dp[i][j],dp[i+1][k-1]+dp[k][j])
判断条件:c[i]==c[k]
感觉好奇妙哇

形式如此的固定,正式比赛时我们是不是可以根据推导出的少量信息蒙呢?

接下来说明转移公式真正的推导:
我们要求dp[i][j]
首先我们知道若i->j所有的c都不一样那么一定是j-i+1
接下来我们想想,对于c[i]如果i+1->j都没有c[i]的话
那么c[i]此时选的衣服一生都不会再次被重复运用了
那么dp[i][j]=dp[i+1][j]+1

如果有的话,这就是我们所说的断点了,其实也就是枚举,我们枚举衣服c[i]被重复利用的点
若c[i]==c[k]
则dp[i][j]=min(dp[i+1][k-1]+dp[k][j],dp[i][j])
意为重复利用。

#include<iostream>
#include<algorithm>
using namespace std;
int c[110];
int dp[110][110];
int n;
int main(){
    int T;scanf("%d",&T);
    for (int tcase=1;tcase<=T;++tcase){
        scanf("%d",&n);
        for (int i=1;i<=n;++i)scanf("%d",&c[i]);
        for (int i=1;i<=n;++i)dp[i][i]=1;
        for (int i=1;i<n;++i){
            if (c[i]==c[i+1])dp[i][i+1]=1;
            else dp[i][i+1]=2;
        }
        for (int len = 2;len<=n;++len){
            for (int i=1,j=i+len-1;j<=n;++i,++j){
                dp[i][j] = dp[i+1][j]+1;
                for (int k=i+1;k<=j;++k){
                    if (c[i]==c[k]){
                        dp[i][j]=min(dp[i][j],dp[i+1][k-1]+dp[k][j]);
                    }
                }
            }
        }
        printf("Case %d: %d\n",tcase,dp[1][n]);
    }
}