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
",dp[n][0]);
    return 0;
}
原文地址:https://www.cnblogs.com/OvOq/p/14853031.html