的确挺神奇的一道题,跟lyc讨论了一会才想清楚正确性。
用dp[x][y]表示完成[x,y]这些舞会至少需要多少衣服。注意这里dp的定义很明确,就是只完成[x,y],之前不需要穿衣服,之后也不需要穿衣服。
那么对应的,答案应该就是dp[1][n]。
我们考虑转移,这道题中,唯一能优化的地方,无非就是我穿了一件衣服,我没脱,过一会又用上了。
那么如果c[x] == c[y]的话,dp[x][y] == dp[x][y - 1],因为最后一件衣服c[y]没用得着重新穿,直接用的c[x]。这里面x,y都是边界,最里面一直藏个衣服显然不影响我中间怎么倒腾。
除了这个情况以外,还有两个情况,一个是我把c[x]脱了,那么dp[x][y] = dp[x][x] + dp[x + 1][y]了。还有一个情况就是我把c[x]留着,等到[x,y]中的某个k正好露出来,用完再脱掉。
两个情况可以用一个for循环统一起来。
这道题,感觉最大的一个体会还是,要搞清楚DP的定义,我只考虑[x,y]部分,那就不去管外面有没有影响,只要每一层的转移和考虑都是周全的,那就没有问题。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int inf = 10000000; int dp[110][110],T,n,cnt,c[110]; int dfs(int l,int r) { if (dp[l][r] != -1) return dp[l][r]; int minn = inf; if (c[l] == c[r]) minn = min(minn,dfs(l,r - 1)); for (int k = l;k <= r - 1;k++) if (c[l] == c[k]) minn = min(minn,dfs(l,k) + dfs(k + 1,r)); return dp[l][r] = minn; } int main() { for (scanf("%d",&T);T;T--) { memset(dp,-1,sizeof(dp)); scanf("%d",&n); for (int i = 1;i <= n;i++) dp[i][i] = 1; for (int i = 1;i <= n;i++) scanf("%d",&c[i]); printf("Case %d: %d ",++cnt,dfs(1,n)); } return 0; }