UVA1579 俄罗斯套娃 Matryoshka(区间DP+序列DP)


题目链接

题面

题目描述

Matryoshkas 指一套传统的,尺寸从小到大套在一起的俄罗斯木娃娃。

一套 Matryoshka 娃娃可以被打开,以便观察其内部一个确定的较小排列。这个排列内部又有更小的一种排列,如此继续下去。

俄罗斯套娃博物馆最近展出了一些类似设计的俄罗斯套娃,它们的唯一不同只是每组套娃内含娃娃的数量。

不幸的是,一些“热心”的(并且显然没有大人监督的)孩子把这些套在一起的娃娃分开,把所有的单个的套娃放在了一排。在这一排中有n个娃娃,每个都有一个整数的大小。你需要在不知道原来的套娃的数量和排列方式的情况下,重新组装这些娃娃。你只知道,每一个完整的套娃由大小为从1~m的连续整数的娃娃组成,m的大小在不同的套娃中可能是不同的。

当你重组娃娃的时候,你必须遵守以下规则:

  • 你只能把套娃(或者单个的娃娃)放在更大的娃娃里
  • 你只能组合相邻的娃娃
  • 一旦一个娃娃被套进一组套娃里,它就不能被转移到另一组套娃里或者被长久地分开。仅仅当两组套娃被合并时,它才可以与一组套娃短暂地分离。

你的时间非常值钱,你也想尽可能快地完成合并的操作。这个任务唯一消耗时间的部分是拆开一个娃娃。所以你想尽可能最小化这个操作的次数。

例如,当合并套娃[1 、2、6] 和 [4]时,这个操作的次数是2,因为你要打开大小为6的和4的;合并[1、2、5]和[3、4]时,这个操作次数是3.

请编写一个程序来计算完成任务所需的最小操作次数。

输入

多组数据,每组数据两行.

第一行有一个整数n,为单行套娃的数量(1<=n<=500);

第二行有n个用空格整数分割的整数,表示每个套娃的尺寸,保证尺寸在1~500之间。

输出

对于每组数据,如果能成功合并,输出合并的最小操作次数;

如果无法合并(因为那些热心的孩子可能拿走了几个娃娃),输出“impossible”(不含引号)

样例输入

7

1 2 1 2 4 3 3

7

1 2 3 2 4 1 3

样例输出

impossible

7

做法

代码

#include 
#include 
#include 
using namespace std;
const int maxN = 505;
int N, a[maxN], dp[maxN][maxN], MAX[maxN][maxN], MIN[maxN][maxN], f[maxN][maxN], F[maxN];
bool b[maxN], rep[maxN][maxN], flag;
inline int merge(int a, int b, int c, int d) {
    return f[b][MIN[c][d]] - f[a - 1][MIN[c][d]] + f[d][MIN[a][b]] - f[c - 1][MIN[a][b]];
}
int main() {
    while(scanf("%d", &N) != EOF) {
        flag = 0;
        memset(dp, 0x3f, sizeof(dp));
        memset(MAX, -0x3f, sizeof(MAX));
        memset(MIN, 0x3f, sizeof(MIN));
        memset(f, 0, sizeof(f));
        memset(F, 0x3f, sizeof(F));
        memset(rep, 0, sizeof(rep));
        for(int i = 1; i <= N; i++) 
            scanf("%d", &a[i]), MAX[i][i] = a[i], MIN[i][i] = a[i], dp[i][i] = 0;
        for(int i = 1; i <= N; i++) {
            for(int j = 1; j <= N; j++) {
                if(j > i) {
                    MAX[i][j] = max(a[j], MAX[i][j - 1]);
                    MIN[i][j] = min(a[j], MIN[i][j - 1]);
                }
                f[i][j] = f[i - 1][j] + (a[i] > j ? 1 : 0);
            }
        }
        for(int len = 2; len <= N; len++) {
            for(int l = 1; l + len - 1 <= N; l++) {
                int r = l + len - 1;
                for(int k = l; k <= r; k++) {
                    if(!b[a[k]]) b[a[k]] = true;
                    else{flag = true; rep[l][r] = true; break;}                    
                }
                memset(b, 0, sizeof(b));
                if(flag){flag = false; continue;}
                for(int k = l; k < r; k++)
                    dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r] + merge(l, k, k + 1, r));
            }
        }
        F[0] = 0;
        for(int i = 1; i <= N; i++)
            for(int j = 0; j < i; j++)
                if(MAX[j + 1][i] == i - j && (!rep[j + 1][i]))
                    F[i] = min(F[i], F[j] + dp[j + 1][i]);
        if(F[N] >= 0x3f3f3f3f)     printf("impossible\n");
        else printf("%d\n", F[N]);
    }
    return 0;
}