网易春招面试题--搬砖

小易有n块砖块,每一块砖块有一个高度。小易希望利用这些砖块堆砌两座相同高度的塔。为了让问题简单,砖块堆砌就是简单的高度相加,某一块砖只能使用在一座塔中一次。小易现在让能够堆砌出来的两座塔的高度尽量高,小易能否完成呢。
输入描述:
输入包括两行:
第一行为整数n(1 ≤ n ≤ 50),即一共有n块砖块
第二行为n个整数,表示每一块砖块的高度height[i] (1 ≤ height[i] ≤ 500000)


输出描述:
如果小易能堆砌出两座高度相同的塔,输出最高能拼凑的高度,如果不能则输出-1.
保证答案不大于500000。

输入例子:
3
2 3 5

输出例子:
5


小易现在让能够堆砌出来的两座塔的高度尽量高,说明是求最优值。适合用DP,一块砖只能用一次就是01背包问题了。

接下来就是想怎么构造最优子结构 dp[i][j]

i肯定是前i个砖块,问题是怎么求当两堆砖块高度相等时,最高的情况。因为高度不等的两堆砖块加上一个砖块之后可能会相等,所以需要记录不相等的详细信息,那么j就是两堆砖块的高度差。在高度差为0的时候,就是两堆砖块高度相等的时候,那么此时两堆砖块任何一堆的高度就是我们相求的值。所以i表示前i个砖块,j表示两堆砖块的高度差,dp[i][j]表示其中一堆的高度

我们设两堆砖块分别为A堆,B堆。j=B堆高度-A堆高度。 dp[i][j]为B堆的高度。 (当然你也可以有其他的设置方法,只要符合红字就可以。)因为题目要求不能堆砌出相同高度时要输出-1,所以对于不可能的情况我们要将值设为-1表示,我们一开始假设所有的情况都不可能(除了dp[0][0] = 0,在0个砖块情况下,两堆砖块高度差为0,且B堆砖块高度为0,这是成立的)。

对于前i个砖块,要想求两堆高度差(B-A)为j时,B堆砖块高度的最大值。有以下几种情况,第一种,可能不使用第i个砖块,当前i-1个砖块,在两堆高度差为j时,B堆砖块高度已经是最大值,此时dp[i][j] = dp[i-1][j]。第二种,需要使用第i个砖块,且需要你把第i个砖块放到A堆时,此时高度差变为j,B堆砖块高度为最大值。设第i个砖块的高度为h[i],那么在前i-1个砖块的时候,高度差为j+h[i] (因为这里我们的高度差是B-A,你把砖块加到A堆时缩小了高度差), 此时因为第i个砖块放到A堆,B堆的高度不变,所以dp[i][j] = dp[i-1][j+h[i]]。第三种,需要使用第i个砖块,且把该砖块放到B堆上,此时高度差变为j,那么原来的高度差为j-hp[i],此时dp[i][j] = dp[i-1][j-h[i]] + h。 我们需要考虑以上三种可能情况,取最大值。

此时的代码如下:

int main()
{
    int n = 0;
    cin >> n;
    vector<int> brick(n+1);
    int sum = 0;
    for (int i = 1; i <= n; ++i) {
        cin >> brick[i];
        sum += brick[i];
    }
    vector<vector<int>> dp(n+1, vector<int>(2 * sum + 1, -1));
    dp[0][sum] = 0;                             //j表示B-A的长度,B-A可能为负,所以偏移sum个长度
    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j <= 2 * sum; ++j) {  
            int h = brick[i];
            dp[i][j] = dp[i - 1][j];            //如果j+h和j-h都不在数组边界内,那么直接使用上一次的结果

            if (j + h <= 2 * sum && dp[i - 1][j + h] != -1) {
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j + h]); //新的砖头放在A,或者不用这个砖头
            }
            if (j - h >= 0 && dp[i-1][j - h] != -1) {
                dp[i][j] = max(dp[i-1][j], dp[i-1][j - h] + h); //新的砖头放在B
            }

        }

    }
    if (dp[n][sum] == 0)
        cout << -1 << endl;
    else
        cout << dp[n][sum] << endl;

    return 0;

}

但使用的内存会超出限制,需要使用滚动数组
因为我们只需要最后前i个砖块的信息,而不需要前i-1个砖块的信息,所以我们可以丢弃那些信息。

使用滚动数组后的代码。


int main()
{
    int n = 0;
    cin >> n;
    vector<int> brick(n + 1);
    int sum = 0;
    for (int i = 1; i <= n; ++i) {
        cin >> brick[i];
        sum += brick[i];
    }
    int f = 1;
    vector<vector<int>> dp(2, vector<int>(2 * sum + 1, -1));
    dp[1-f][sum] = 0;                               //j表示B-A的长度,B-A可能为负,所以偏移sum个长度
    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j <= 2 * sum; ++j) {
            int h = brick[i];
            dp[f][j] = dp[1-f][j];          //如果j+h和j-h都不在数组边界内,那么直接使用上一次的结果

            if (j + h <= 2 * sum && dp[1-f][j + h] != -1) {
                dp[f][j] = max(dp[f][j], dp[1-f][j + h] ); //新的砖头放在A,或者不用这个砖头
            }
            if (j - h >= 0 && dp[1-f][j - h] != -1) {
                dp[f][j] = max(dp[f][j], dp[1 - f][j - h] + h); //新的砖头放在B
            }

        }

        f = 1 - f;

    }
    if (dp[1-f][sum] == 0)
        cout << -1 << endl;
    else
        cout << dp[1-f][sum] << endl;


    return 0;

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值