牛客-[HAOI2012]音量调节音量调节——01背包问题或者回溯记忆化

题目

题目链接

题目解析

首先根据题目的意思,我们很快可以建立起一个逻辑:

给一组数据添 + 或者 - 号,然后要让它们得到的最终结果尽量的大!

但注意啊,中间不能存在吉他低于0或者高于maxLevel的情况!否则吉他可能报废,我觉得差不多应该是这么个意思。

故很快啊,写个回溯,然后很快啊,就会超时。。。这个时候再对回溯过程加一个记忆化即可,实际上有用的可能就是中间出现0的时候可以优化它不继续重复计算了。其他情况这个记忆化等于没用,但正好就是这个对 +-0 情况的记忆化让这道题就能过了!

以下为记忆化的解题代码

#include<bits/stdc++.h>
using namespace std;

const int maxn = 105;
int n, beginLevel, maxLevel, ans = -maxn;
int a[maxn];

int dp[maxn][1005];//TODO 这个记忆化主要针对重复性动作,比如+0或-0是一样的
bool solve(int curLevel, int t) {//curLevel表示当前音量,t表示第t首曲子
    if (curLevel > maxLevel || curLevel < 0) {
        return false;//TODO 不能在选择途中出现吉他低于0或者高于maxLevel的情况!
                     // 否则吉他可能报废,差不多应该是这么个意思
    }
    if(dp[t][curLevel]!=-1)return dp[t][curLevel];
        //已经是最后一首曲子,更新最大值ans
    if (t == n + 1) {
        ans = max(ans, curLevel);
        return true;
    }
    bool plus = solve(curLevel + a[t], t + 1);
    bool minus = solve(curLevel - a[t], t + 1);
        //若尝试两种情况都失败了
    if (!plus && !minus) {
        return dp[t][curLevel] = false;
    }
    return dp[t][curLevel] = true;
}

int main() {
    ios::sync_with_stdio(false);
    cin>>n>>beginLevel>>maxLevel;
    for (int i = 1; i <= n; i++) {
        cin>>a[i];
    }
    memset(dp,0xff,sizeof(dp));
    if (solve(beginLevel, 1)) {
        cout<<ans<<'\n';
    } else {
        cout<<-1;
    }

    return 0;
}

从最终的情况来说,这题考察的实际上应该是背包问题,是01背包问题,只不过这个背包可以加也可以减去这个值。

对于背包问题来说,也就在于物品的种类以及取与不取而已。

这部分直接看解题代码即可。

解题代码

#include <bits/stdc++.h>
using namespace std;
int nums[55];
bool dp[55][1005];//TODO 如果dp[i][j]为true表示j在前i个物品的加持下能够拼出
int n;
int ret = -1;
int mx,mn;

int main () {
    ios::sync_with_stdio(false);
    cin>>n>>mn>>mx;
    for (int i = 1; i <= n; ++i) {
        cin>>nums[i];
    }
    //TODO 开始01背包更新
    dp[0][mn] = 1;//TODO 当不选择任何物品时,可以得到最低音
    for(int i=1;i<=n;i++){
        for(int j=0;j<=mx;j++){
            if(j>=nums[i])//TODO 背包取加法
                dp[i][j] |= dp[i-1][j-nums[i]];
            if(j+nums[i]<=mx)//TODO 背包取减法
                dp[i][j] |= dp[i-1][j+nums[i]];
        }
    }
    //TODO 搜索能够取到的的最大值
    for(int p=mx;p>=0;p--)
        if(dp[n][p])
        {
            ret = p;
            break;
        }
    cout<<ret;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值