LDUOJ——E. 塔(dp+状态的选择)

简介: LDUOJ——E. 塔(dp+状态的选择)

E. 塔

Description

给出 NN 个木块,告诉你每块木块的高度,你要用这些木块搭出两座高度相同的塔,一座塔的高度为搭建它的木块的 高度和,并且一座塔至少要用一块木头。木块只能用一次,也可以不用。问在两座塔的高度相同的限制下,能够搭 的塔的最大高度是多少?

Input

第一行一个整数 NN,表示木块个数; 第二行 NN 个整数,表示 NN 块木块的高度。

Output

仅一个数,表示能搭建的最高的塔的高度,若不能搭建两座相同高度的塔,输出-1。

Samples

Input [复制](javascript:)

3
2 3 5

Output

5

Hint

N≤50N≤50,每块木块的高度范围[1,500000],所有木块的高度总和≤500000≤500000。

思路:


一个很巧妙的dp。

看了看别人的题解发现起名叫差值dp(好高大上)

假设dp[i][j]表示选到第i个物品并且高塔和矮塔的差值为j时高塔最高的高度。

接下来看转移,假设现在已经处理好了前i-1个物品的状态,考虑第i个物品如何选择:

1.不选择第i个物品,前一个状态就是dp[i-1][j]

2.将第i个物品给矮塔,说明第i个物品的贡献是减少了a[i]的差值,也就是说上一个状态是dp[i-1][j+a[i]]。由于给了矮塔,所以dp的数值是不会变的。

3.将第i个物品给高塔,说明第i个物品的贡献是增加了a[i]的差值,也就是说上一个状态是dp[i-1][j-a[i]]。由于给了高塔,对应dp的数值也要改变。这个状态有效转移的前提是j≥a[i]。

第一次的时候就想到了这些,然后wa了。

其实上面三种情况都有一个统一的结果就是高塔和矮塔的顺序没有调换,但是如果第i个物品有足够的贡献,矮塔可能就会变成高塔。接下来分析一下这种情况的转移:

假设处理好前i-1个状态后,两堆物品的高度分别为A和B。

假设在i-1时,A是高塔,B是矮塔。

在i时,B的高度变成了B+a[i],B是高塔,A是矮塔。

那么dp[i][j]表示的是B+a[i]-A=j。

现在考虑前一个状态,即i-1时:这时候的差值为A-B,根据上一行的等式,A-B=a[i]-j,所以前一个状态为dp[i-1][a[i]-j]。

再来考虑对dp数值的贡献:以前的高塔高度为A,现在的高塔高度为B+a[i],所以贡献为j。

注意初始化和答案不存在的情况。


代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll,ll>PLL;
typedef pair<int,int>PII;
typedef pair<double,double>PDD;
#define I_int ll
inline ll read()
{
    ll x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
#define read read()
#define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define multiCase int T;cin>>T;for(int t=1;t<=T;t++)
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define perr(i,a,b) for(int i=(a);i>(b);i--)
ll ksm(ll a,ll b,ll p)
{
    ll res=1;
    while(b)
    {
        if(b&1)res=res*a%p;
        a=a*a%p;
        b>>=1;
    }
    return res;
}
#define PI acos(-1)
const double eps=1e-10;
const int maxn=1e6+100;
int n,dp[55][500010],a[55];
int main()
{
    n=read;
    int sum=0;
    rep(i,1,n)
        a[i]=read,sum+=a[i];
    memset(dp,-0x3f,sizeof dp);
    dp[0][0]=0;
    for(int i=1;i<=n;i++){
        for(int j=0;j<=sum;j++){
            int t=max(dp[i-1][j],dp[i-1][j+a[i]]);///不选第i个,第i个给矮塔
            dp[i][j]=max(t,dp[i][j]);
            if(j>=a[i]){///放在高塔上
                dp[i][j]=max(dp[i][j],dp[i-1][j-a[i]]+a[i]);
            }
            ///第i个给矮塔后 矮塔成为高塔
            if(a[i]>j){
                dp[i][j]=max(dp[i][j],dp[i-1][a[i]-j]+j);
            }
        }
    }
    if(dp[n][0]<=0) puts("-1");
    else printf("%d\n",dp[n][0]);
    return 0;
}




目录
相关文章
|
6月前
|
算法
【算法】—— 简单多状态 dp 问题
【算法】—— 简单多状态 dp 问题
|
6月前
流(树形dp,换根dp)
流(树形dp,换根dp)
30 0
|
6月前
【每日一题Day357】LC1155掷骰子等于目标和的方法数 | dp
【每日一题Day357】LC1155掷骰子等于目标和的方法数 | dp
55 0
|
1月前
动态规划——状态 dp
本文介绍了多个动态规划问题的解法,包括按摩师问题(即打家劫舍),通过 `dp` 数组追踪选与不选的最大收益;打家劫舍 II 将环形问题转化为线性;删除并获得点数问题,将数组处理后按打家劫舍规则求解;粉刷房子问题,使用三维 `dp` 数组追踪每种颜色的最小成本,每个问题都详细说明了状态表示、转移方程及初始化等关键步骤,并附有代码实现。
21 2
|
6月前
树形dp常见类型——换根dp
树形dp常见类型——换根dp
|
6月前
距离和(换根dp)
距离和(换根dp)
61 0
|
6月前
|
人工智能
daimayuan 国家铁路(前缀和,dp)代码源
daimayuan 国家铁路(前缀和,dp)代码源
38 0
|
机器学习/深度学习 定位技术
【每日一题Day109】LC1210穿过迷宫的最少移动次数 | BFS+dp
思路:使用BFS搜索,队列中存放三元组[蛇尾的横轴坐标x,蛇尾的纵轴坐标y,蛇的状态],当蛇为水平时,状态为0;当蛇为竖直时,状态为1
125 1
【每日一题Day109】LC1210穿过迷宫的最少移动次数 | BFS+dp
|
存储 C++
C++/PTA 求两点之间距离
定义一个Point类,有两个数据成员:x和y, 分别代表x坐标和y坐标,并有若干成员函数。 定义一个函数Distance(), 用于求两点之间的距离。
225 0