题面
题目描述
一个吉他手准备参加一场演出。他不喜欢在演出时始终使用同一个音量,所以他决定每一首歌之前他都需要改变一次音量。在演出开始之前,他已经做好一个列表,里面写着每首歌开始之前他想要改变的音量是多少。每一次改变音量,他可以选择调高也可以调低。
音量用一个整数描述。输入文件中整数 beginLevel,代表吉他刚开始的音量,整数 maxLevel,代表吉他的最大音量。音量不能小于 0 也不能大于maxLevel。输入中还给定了 n 个整数 1,2,3,⋯ ,c1,c2,c3,⋯,cn,表示在第 i 首歌开始之前吉他手想要改变的音量是多少。
吉他手想以最大的音量演奏最后一首歌,你的任务是找到这个最大音量是多少。
输入格式
第一行依次为三个整数 n,beginLevel 和maxLevel。
第二行依次为 n 个整数 1,2,3,⋯ ,c1,c2,c3,⋯,cn。
输出格式
输出演奏最后一首歌的最大音量。如果吉他手无法避免音量低于 0 或者高于 maxLevel,输出 -1
。
输入输出样例
输入 #1
3 5 10 5 3 7
输出 #1
10
说明/提示
1≤≤n≤50,1≤≤1≤ci≤beginLevel,1≤maxLevel≤1000,0≤≤beginLevel≤maxLevel。
分析:
这一道题是一道典型的dp背包题,题中说他已经做好一个列表,里面写着每首歌开始之前他想要改变的音量是多少,所以这是01背包问题
状态转移方程 :
1.先考虑二维的01背包,由题可知,音量调节最多能调50次,maxLeve最大为1000,二维
数组能开那么大,可采取
2.再考虑一维的01背包,只有时间维度,数组能开,但有限制,一维太过片面,不可以使用
3.考虑交替滚动,,此题跟一维一样,不可以使用
综上所述,本题采用二维的01背包!
先推二维01背包:
本题需分类讨论,如果0<音量减降低的音量,那么可调低音量,标记等于上一次能否调音量并能继续调本次音量||本次能否调音量,如果音量加调高的音量<maxLevel,那么可调高音量,标记等于上一次能否调音量并能继续调本次音量||本次能否调音量,也就是:
for(int i=1;i<=n;i++){
for(int j=r;j>=0;j--){
if(j-w[i]>=0) dp[i][j]|=dp[i-1][j-w[i]];
if(j+w[i]<=r) dp[i][j]|=dp[i-1][j+w[i]];
}
}
注意:
最后不能像普通01背包那样直接输出dp[n][r],而是要从后往前遍历第n行,如果为true就立刻输出,并结束程序,如果遍历结束都没输出,就输出0
顺序:
第一个for循环遍历调节音量数量,第二个for循环遍历当前音量,注意要从后往前遍历不然就成了完全背包了
代码:
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<string>
#include<vector>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#include<algorithm>
#define ll long long
using namespace std;
int dp[55][1050],n,l,r,w[55];
int main(){
cin>>n>>l>>r;
for(int i=1;i<=n;i++){
cin>>w[i];
}
dp[0][l]=1;
for(int i=1;i<=n;i++){
for(int j=r;j>=0;j--){
if(j-w[i]>=0) dp[i][j]|=dp[i-1][j-w[i]];
if(j+w[i]<=r) dp[i][j]|=dp[i-1][j+w[i]];
}
}
for(int i=r;i>=0;i--){
if(dp[n][i]==1){
cout<<i;
return 0;
}
}
cout<<-1;
return 0;
}
本题是01背包问题,如果在练习01背包问题,可以做洛谷的P1616(疯狂的采药):
https://www.luogu.com.cn/problem/P1616
可参考题解(洛谷P1616 疯狂的采药题解(精品)):
https://blog.csdn.net/dldltangmen/article/details/140694015?spm=1001.2014.3001.5501