题目链接:
LightOJ 1422 Halloween Costumes
题意:
需要去参加 n 个聚会,每个聚会可能需要穿不同的衣服,用数字编号表示( 1−100 )。如果连续的聚会需要穿的衣服一样,那就不用换衣服,也可以选择在身上的衣服外面再套上新的衣服,但是脱下的衣服不能在用于剩下的聚会的了,问最少需要准备多少件衣服?
数据范围: n≤100
分析:
先解释下样例。
n=41 2 1 2
意思是第一场和第三场聚会需要穿1号衣服,第二场和第四场聚会需要穿2号衣服,那么可以选择在第一场聚会时穿上1号衣服,在第二场聚会时在1号衣服外面套上2号衣服,在第三场聚会时脱下外面的2号衣服,然后第四场聚会时在套上一件新的2号衣服,那么总共至少3件衣服。
区间 dp 。
用 dp[i][j] 表示从第 i 场聚会到第 j 场聚会最少需要的衣服数量。区间最优可由子区间最优递推得到。可以发现如果把子区间独立看的话,子区间是不存在联系的,但是当合并成父区间时需要考虑第一个首尾位置的数字编号是否相同,如果相同的话那么就可以节省一件衣服,因为可以选择将这件衣服一直保留在最里层,当在第二个区间需要最后一次需要穿上这件衣服时将外面所有的衣服脱掉即可。所以有状态转移方程:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
using namespace std;
typedef long long ll;
const int MAX_N = 110;
const int inf = 0x3f3f3f3f;
int T, n, cases = 0;
int data[MAX_N], dp[MAX_N][MAX_N];
int main()
{
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
for (int i = 0; i < n; ++i) {
scanf("%d", &data[i]);
}
for(int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
dp[i][j] = inf;
}
}
for (int i = 0; i < n; ++i) { dp[i][i] = 1; }
for (int i = n - 1; i >= 0; --i) {
for (int j = i; j < n; ++j) {
for (int k = i; k < j; ++k) {
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j]);
}
if(i != j && data[i] == data[j]) dp[i][j]--;
//if(dp[i][j] == 0) dp[i][j] = 1;
// printf("dp[%d][%d] = %d\n", i, j, dp[i][j]);
}
}
printf("Case %d: %d\n", ++cases, dp[0][n - 1]);
}
return 0;
}