题意:一个人先后要参加n个舞会,已知他n个舞会要穿什么样的衣服。一件衣服他脱了以后,就不会再穿。他可以穿了一种衣服以后,在外面套另一种衣服,再在外面套另一种衣服........。问他最少要准备多少件衣服?
题解:原问题是参加完区间[1,n]的舞会,最少要准备多少件衣服。如果第1个舞会要穿的衣服和第i个舞会要穿的衣服相同,假设我们的策略是让第1个舞会和第i个舞会穿同一件衣服,那么区间[2,i-1] 的舞会所穿的衣服最后都要脱光。问题就变成求,区间[2,i-1]最少要准备多少件衣服,加上区间[i,r]最少要准备多少件衣服。原问题就转变成求子问题区间[2,i-1]和区间[i,r] 。当然我们也可以让第1个舞会穿的衣服以后都不穿,问题就转变成求子问题区间 [2,r] 最少要准备多少件衣服+1。显然不同的策略,会有不同的子问题,一定存在重叠子问题,而且子问题没有后效性。所以我们可以用动态规划来解决。
用dp[l][r] 表示参加[l,r] 的舞会最少要准备多少件衣服。转移就是:
dp[l][r]=min(dp[l][r],dp[l+1][r]+1)
如果第k件衣服和第l件衣服相同:
dp[l][r]=min(dp[l][r],dp[l+1][k-1]+dp[k][r]);
详情见代码:
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
#include<string>
#include<math.h>
#define nn 110
#define inff 0x3fffffff
#define eps 1e-8
typedef long long LL;
const LL inf64=LL(inff)*inff;
using namespace std;
int n;
int a[nn];
int dp[nn][nn];
int dfs(int l,int r)
{
if(dp[l][r]!=-1)
return dp[l][r];
if(l>r)
return 0;
if(l==r)
return dp[l][r]=1;
dp[l][r]=inff;
dp[l][r]=min(dp[l][r],dfs(l+1,r)+1);
for(int i=l+1;i<=r;i++)
{
if(a[i]==a[l])
dp[l][r]=min(dp[l][r],dfs(l+1,i-1)+dfs(i,r));
}
return dp[l][r];
}
int main()
{
int i,t;
scanf("%d",&t);
int cas=1;
while(t--)
{
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
memset(dp,-1,sizeof(dp));
printf("Case %d: ",cas++);
printf("%d\n",dfs(1,n));
}
return 0;
}