题意: 一个人要去参加n个晚会, 一个长度为n的序列给出每个晚会要穿的衣服类型, 这个人可以套着穿无数件, 但是只要脱下来就必须换新衣服. 问最少这个人要消耗多少件衣服.
思路:
区间dp维护区间内, 这个人最少消耗多少件衣服.
长度为1的区间一定是1.
①长度为x的区间, 先赋为x, 代表最差情况下, 每到一个晚会就换一件新的衣服.
②当这个区间的头尾相同时, 可以只消耗长度为x-1的子区间的代价.
③其他以这个区间的每个位置为分界点, 枚举更新最小代价.
递推式:
①tmp=len
②tmp=dp[s][t-1] if(...)
③tmp=min(dp[i][k]+dp[k+1][j]) for k in i to j
另外, 众所周知, 区间dp要先枚举长度小的.
所以当枚举到XXXL长度, XXL长度一定已经枚举完了, 所以区间dp的当前区间一定是可以只由两个子区间递推的.
代码:
//#include<bits/stdc++.h>
#include <cstdio>
#include <iostream>
using namespace std;
void debug_out() {
cerr << '\n';
}
template<typename T, typename ...R>
void debug_out(const T &f, const R &...r) {
cerr << f << " ";
debug_out(r...);
}
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]: ", debug_out(__VA_ARGS__);
typedef long long ll;
const int M = 505;
const int inf = 1e9 + 5;
const int mod = 1e9 + 7;
int n;
int dp[M][M];
int a[M];
int main() {
int _, caz = 0;
scanf("%d", &_);
while (_--) {
scanf("%d", &n);
fill(dp[0], dp[0] + M * M, inf);
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
for (int len = 1; len <= n; len++) {
for (int s = 0; s < n; s++) {
int t = s + len - 1;
if (s == t) {
dp[s][t] = 1;
continue;
}
int tmp = len;
// 1 1
if (s + 1 == t && a[s] == a[t])
tmp = min(tmp, 1);
if (s + 1 <= t - 1 && a[s] == a[t]) {
tmp = min(tmp, dp[s][t - 1]);
// 因为a[s]==a[t], 所以dp s t就等于dp s+1 t或者 dp s t-1
}
for (int i = s; i + 1 <= t; i++) {
tmp = min(tmp, dp[s][i] + dp[i + 1][t]);
}
dp[s][t] = tmp;
}
}
/* for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
printf("[%d %d]:%d ", i, j, dp[i][j]);
}
puts("");
}*/
printf("Case %d: %d\n", ++caz, dp[0][n - 1]);
}
return 0;
}