NKOJ-1017 搭建双塔

P1017 搭建双塔

时间限制 : 3000 MS 空间限制 : 65536 KB

问题描述
2001年9月11日,一场突发的灾难将纽约世界贸易中心大厦夷为平地,Mr. F曾亲眼目睹了这次灾难。为了纪念“911”事件,Mr. F决定自己用水晶来搭建一座双塔。
Mr. F有N块水晶,每块水晶有一个高度,他想用这N块水晶搭建两座有同样高度的塔,使他们成为一座双塔,Mr. F可以从这N块水晶中任取M(1≤M≤N)块来搭建。但是他不知道能否使两座塔有同样的高度,也不知道如果能搭建成一座双塔,这座双塔的最大高度是多少。所以他来请你帮忙。
给定水晶的数量N(1≤N≤100)和每块水晶的高度Hi(N块水晶高度的总和不超过2000),你的任务是判断Mr. F能否用这些水晶搭建成一座双塔(两座塔有同样的高度),如果能,则输出所能搭建的双塔的最大高度,否则输出“Impossible”.

输入格式
输入的第一行为一个数N,表示水晶的数量。第二行为N个数,第i个数表示第i个水晶的高度。

输出格式
输出仅包含一行,如果能搭成一座双塔,则输出双塔的最大高度,否则输出一个字符串“Impossible”。

样例输入
5
1 3 4 5 2

样例输出
7

自追加样例

Input
3
10 4 7

Output
Impossible

先不谈正事

这代题目不得不说真的是一时手贱点了进去,然后就进入了双背包的奇妙世界
然而...
    说出来你可能不信,我单背包骗了90分,一开始以为那10分没得是Impossible拼错了哈哈
    附上骗分代码(思想还是很精妙,但是用我追加的数据是可以卡掉的)
//然而我并不知道哪里来的脸说这段代码思想精妙
#include <iostream>
#include <cstdio>
using namespace std;

bool built[2345]={1};

int main()
{
    int n,k,maxi=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&k);
        for(int i=maxi;i>=0;i--)
            if(built[i])built[i+k]=1;
        maxi+=k;
    }
    for(int i=(maxi&1)?maxi-1:maxi;i>=2;i=i-2)
        if(built[i]&&built[i/2]){cout<<(i/2);return 0;}
    printf("Impossible");
}

说回正事

这道题目思想很简单,给一块石头,三种选择,放在塔one上,或者塔two,或者不放
然而很明显的是
    思路很简单,状态很难定(因为如果定res[one][two]的话,你不管先加哪个都没法处理一块石头同时加到两座塔上的情况)
    所以我无耻地看了题解,然后来写博客,发扬学长的精神

我们规定状态res[n][delta]:对于第n块石头,两座塔的高度差为delta,而res[n][delta]记录的是较低的那座塔的高度
于是,我们当前的状态是继承的[n-1]的状态,就不会出现一块石头同时加到两座塔上的情况
然后遍历
对于第n块石头,高度为stone,遍历delta

那么就上面的思路来思考
①不放
    最简单
    res[n][delta]=res[n-1][delta]
②放到高的塔上
    也很简单,就是高度差变大了,高度较低的那座塔高度并没有变
    res[n][delta+stone]=res[n-1][delta]
③放到低的塔上
    此时需要判断
    a.低塔加了石头之后还是比高塔低(那就是先天不足了...)
      那么此时高度差减少为delta-stone(但还是长高了)
        res[n][delta-stone]=res[n-1][delta]+stone
    b.低塔加了石头之后比高塔高(农奴翻身把歌唱)
      于是高塔变成了低塔且高度为res[n-1][delta]+delta
      高度差变为了stone-delta(三十年河东三十年河西啊)
        res[n][stone-delta]=res[n-1][delta]+delta
以上所有值都要和原值比较

我觉得说的这么详细了应该还是很容易懂的
因为是遍历,所以不会漏掉任何情况(除非是相同条件下低塔高度较低)
因为是继承,所以不会发生叠加时的冲突

附上代码

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;

bool k;
int n,res[2][2345],stone,delta=0;//滚动数组优化

int main()
{
    memset(res,-1,sizeof(res));
    scanf("%d",&n);
    delta=res[0][0]=0;
    for(int i=1;i<=n;i++)
    {
        k=i&1;
        scanf("%d",&stone);
        for(int pos=delta;pos>=0;pos--)
            if(res[k^1][pos]!=-1)
            {
                if(pos>=stone)res[k][pos-stone]=max(res[k][pos-stone],res[k^1][pos]+stone);
                else res[k][stone-pos]=max(res[k][stone-pos],res[k^1][pos]+pos);
                res[k][pos+stone]=max(res[k][pos+stone],res[k^1][pos]);
                res[k][pos]=max(res[k][pos],res[k^1][pos]);
            }
        delta+=stone;
    }
    if(res[n&1][0])cout<<res[n&1][0];
    else printf("Impossible");
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值