LightOJ 1422 Halloween Costumes(区间dp)

题目大意: 给你n天要穿的衣服,可以套着穿,但是一旦脱下来就不能载穿了,问n天至少需要多少衣服?

做了两个关于区间dp的题,遇到这个题还是不会做,网上搜了一下,感觉也不难,就是自己想不起来。。。

思路:dp[i][j]表示从第i天到第j天至少需要多少衣服,那么dp[i][j] = dp[i + 1][j] + 1,这是最坏情况,就是最多的一种,如果后面有跟第i个相同的,那么根本不许要这么多,只需要dp[i+1][j]中就够了,不用加这个1了,因为后面有跟他相同的,考虑第k天,那么就假设第k天与第i天相同,那么dp[i+1][j]就可以表示为dp[i+1][k-1] + dp[k][j], dp[i + 1][k - 1]表示从i+1天到k-1天最小需要,因为这个和k无关,所以不用管里面有没有相同的,但是后面这个dp[k][j]就有用了,就是说必须k天的跟i天的相同才可以这么拆,不然不能拆。所以状态转移方程就有了。dp[i][j]=min(dp[i][j], dp[i + ][k - 1] + dp[k][j]);

代码如下:

#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;
const int maxn = 110;
const int inf = (1 << 30);
int dp[maxn][maxn];
int a[maxn];
int main()
{
    int T, n, kase = 0;
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        for (int i = 1; i <= n; i++)//初始化
            for (int j = i; j <= n; j++)
                dp[i][j] = j - i + 1;
        for (int i = n - 1; i >= 1; i--)//从后往前推
        {
            for (int j = i + 1; j <= n; j++)
            {
                dp[i][j] = dp[i + 1][j] + 1;
                for (int k = i + 1; k <= j; k++)
                    if (a[i] == a[k])
                        dp[i][j] = min(dp[i][j], dp[i + 1][k - 1] + dp[k][j]);
            }
        }
        printf("Case %d: %d
", ++kase, dp[1][n]);
    }
    return 0;
}
View Code

还有一点需要说明,在dp问题中,循环顺序是非常非常重要的,一定要搞清楚当前状态是从哪个状态过来的。而且从那个状态过来的时候,那个状态一定是计算过的。上面的就是从后往前推。下面还有一种解法,我个人感觉是非常自然的一个解法,就是状态转移方程比较贴切。

用dp[i][j]表示从第i天到第j天至少需要多少衣服,考虑第j天,如果在第j天的时候需要的衣服在i->j-1天里面如果没有就是dp[i][j] = dp[i][j - 1] + 1; 如果已经有了的话,那么dp[i][j] = dp[i][j - 1]; 假设在第k天和第j天的衣服相同,由于dp[i][j - 1] = dp[i][k] + dp[k + 1][j - 1]; 所以dp[i][j] = dp[i][k] + dp[k+1][j - 1];

代码如下:

#include <cstdio>
#include <iostream>

using namespace std;

const int maxn = 110;
int dp[maxn][maxn];
int a[maxn];
int main()
{
    int T, n, kase = 0;
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        for (int i = 1; i <= n; i++)
            for (int j = i; j <= n; j++)
                dp[i][j] = j - i + 1;
        for (int j = 2; j <= n; j++)//区间右端点,顺序枚举
        {
            for (int i = j - 1; i >= 0; i--)//左端点逆序
            {
                dp[i][j] = dp[i][j - 1] + 1;
                for (int k = i; k < j; k++)
                    if (a[k] == a[j])
                        dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j - 1]);
            }
        }
        printf("Case %d: %d
", ++kase, dp[1][n]);
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/Howe-Young/p/4737536.html