LOJ-1422 万圣节服装

题意(大概翻译):

小憨憨 参加圣诞节的一些派对,并且需要穿上对应派对的衣服,所以他需要多次换衣服,为了方便,他可以选择脱掉一些衣服或者穿上新衣服,比如说,他穿着 美队 的衣服,外面又穿着 钢铁侠 的衣服,当他要参加 美队 服装派对时,他可以选择脱掉 钢铁侠 的衣服(因为 钢铁侠 衣服的里面有 美队 的衣服),或者他可以在穿一件 美队 的衣服,小憨憨 是个爱干净的人,当他脱下 钢铁侠 的衣服后,如果需要再穿 钢铁侠 的衣服,他会选择再穿一件新的。(如果他先穿A衣服,又穿上B衣服,再穿一件C衣服,如果他想让最外面的衣服是A,他可以选择直接穿一件A,或者先把C脱掉,再把B脱掉)。

输入:

2

4

1 2 1 2

7

1 2 1 1 3 2 1

输出:

Case 1: 3

Case 2: 4

思路: 假设参加 n个派对, 存在i,j使得 1 <= i <= j <= n。啥都不考虑的情况下,那么从i~j就需要  j-i+1件衣服(第一个派对也要穿,即一个派对换一件衣服),根据这个情况来赋初值 dp[i][j] = j-i+1;

然后再考虑状态转移方程,即 从i~j个派对会不会有重复的派对k(衣服k)出现,即出现a[k] = a[i] (k > i),即可以少一件衣服 ,那么得到递推方程为 :

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

代码如下:

#include<cstdio>
#include<cstring>
#define min(a,b) ((a) < (b)? (a):(b))
const int maxn = 110;
int dp[maxn][maxn],a[maxn];
int n,t,cas;
int main(){
  scanf("%d",&t);
  for(cas =0 ;cas < t ; cas++){
    scanf("%d",&n);
    for(int i=1 ;i<=n ;i++)
    scanf("%d",&a[i]);
    for(int i=1 ;i<=n ;i++){    //n个聚会
      for(int j=i ;j<=n ;j++)    //从i到j
       dp[i][j] = j-i + 1;       //赋初值,啥都不干的情况下从i~j要j-i+1件衣服
    }
    for(int i=n-1 ;i>=1 ;i--)  //反着来,第一行的就是最小的
      for(int j=i ;j<=n ;j++){
        dp[i][j] = dp[i+1][j]+1;   //分割为两段,坐标为i单独一段,i+1~j为一段
        for(int k=i+1 ;k<=j ;k++) //i~j中是否存在这么一个k,a[k]=a[i]
         if(a[k] == a[i])
         dp[i][j] = min(dp[i][j], dp[k][j]+ dp[i+1][k-1]);   //递推方程
      }
      printf("Case %d: %d
",cas+1, dp[1][n]);
  }
  return 0;
}
原文地址:https://www.cnblogs.com/wxx23-IOU/p/13561823.html