ZOJ 2059 —— The Twin Towers 解题报告

据说很经典的DP题目。来源浙大月赛。

参考解题资料:http://allen303allen.bokee.com/viewdiary.19263200.html

 

自己的代码:

/*
ZOJ 2059 —— The Twin Towers
给定一个数字序列,问能不能构成2个序列,使得这两个序列的和相同,序列中的数字可以不用完。输出构成的2个序列的最大长度。
对于每个数字有3种选择,要么不放,要么放到较低的塔上,要么放到较高的塔上。
那么状态怎么表示呢?看到题目中塔的最大高度为2000,如果枚举这三种情况加100个数据就是3^100,肯定gg了。
从别人那学到的一种表示状态的方法,可以将2000看做最大高度差,那么共有2001个状态了。
对于每个状态的定义就是在高度差为i的情况下,较高的塔的高度是多少。
对于在高度差一定时,可以选择将当前数字添加到较高的塔上还是较低的塔上,然后就是结果,如果添加到较低的塔上会有2种情况:
1. 还是比高塔低   
2. 比高塔高了 , 更新新的高度差下的最大塔高, 
向高塔中添加此数,可以正常更新。
状态转移方程:h[i]为第i个数字,dp[i][j]为用到第i个数字时,高度差为j时较高的塔的高度 
    dp[i][j+h[i]] = max(dp[i][j+h[i], dp[i-1][j] + h[i]); //向高塔上添加此数。 
    if ( h[i] > j ) //h[i]比高度差j大,所以添加h[i]后就比dp[i-1][j]里的最高塔还高了。
        dp[i][h[i]-j] = max(dp[i][h[i]-j], dp[i-1][j] + h[i]-j);
    else           //h[i]比高度差j小 ,所以添加h[i]后较高的塔还较高, 
        dp[i][j-h[i]] = max(dp[i][j-h[i]], dp[i-1][j] ); 
做的时候运用循环数组,节省空间,注意初始化,在j高度差下,没有数字时要初始化成一个状态。 
*/
#include <iostream>
#include <cstdio>


int max(const int a, const int b)
{
    if ( a > b )
        return a;
    else
        return b;
}
int min(const int a, const int b)
{
    if ( a > b )    return b;
    else    return a;
} 
int main()
{
    int dp[2][2001], a[101];
    int n, i, j;
    int sum ;
    int t;
    while ( scanf("%d", &n ) != EOF && n != -1 )
    {
        sum = 0;
        for ( i = 1; i <= n; ++i )
        {    
            scanf("%d", &a[i]);
            sum += a[i];
        }
        sum = min(sum, 2000);
        for ( i = 0; i <= sum; ++i )
            dp[0][i] = -1;
        dp[0][0] = 0;
        dp[0][a[1]] = a[1];
        t = 0;
        for ( i = 2; i <= n; ++i )
        {
            t = 1-t;
            for ( j = 0; j <= sum; ++j )
                dp[t][j] = -1;
            for ( j = 0; j <= sum; ++j )
            {
                dp[t][j] = max(dp[t][j], dp[1-t][j]);
                if ( dp[1-t][j] == -1 )
                    continue;
                if ( j + a[i] <= sum ) //加到高的tower上 
                    dp[t][j+a[i]] = max(dp[1-t][j]+a[i], dp[t][j+a[i]]);     
                if ( j >= a[i] ) //加到低的上面比高的低 
                {
                    dp[t][j-a[i]] = max(dp[t][j-a[i]], dp[1-t][j]);
                }
                else //加到低的上面比高的高 
                {
                    dp[t][a[i]-j] = max(dp[t][a[i]-j], dp[1-t][j] + a[i]-j); 
                }
                
            }
            
        }
             
        
        
        if ( dp[t][0] > 0 )
            printf("%d\n",dp[t][0]);
        else
            printf("Sorry\n");      
    }
    return 0;
}
 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值