题目链接 https://nanti.jisuanke.com/t/A2012
题目大意是 有 n 个整数, m 个 运算符 , 运算符只包含有 + ,- ,*, / 这四种,然后给你一个初始值 K ,你要依次用这m个运算符对n个数中的m个数进行运算,选择数时只能从前到后,不能回头,但可以越过。也就是说从 n 个整数中选出 m个数, 然后依次对 给出的运算符序列进行相应的运算,使得到的值最大。
这应该是典型的01背包问题? 对第 i 个数选择取或者不取。 我们可以倒着从最大值是如何得到的来考虑这个题目。
设 dp[i][j] 表示 前 i 个数字中用前j个运算符所得出的最大值,所以通常情况下我们可以得出一个dp状态转移式:
dp[i][j] = max(dp[i-1][j],dp[i-1][j-1]+w[i]);
但是分析一下问题我们就可以得出这个式子是不完善的,因为选取第i个数字得到的最大值,不一定是由上个最大值得来的,因为可K值和n值都存在负数,负负得正了..。假如上一步最小值是-1000,最大值是1000 然后 第 i 个 数字是-1 然后就变成了1000.所以我们在比较时,不仅需要保存最大值也需要保存最小值,所以可以开两个dp数组一个用来存最大值,一个用来存最小值,或者开一个三维数组,两种都可以,然后更新值的时候就先分别求出上一步最大值和最小值对第 i 个数进行第 j 个运算符操作时的值,然后在将两者中大的值保存在 dpmax中 小的保存在 dpmin 中
AC代码:
#include<iostream>
#include<cstring>
using namespace std;
#define INF 999999999
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long ll;
int N,M,k;
ll a[1005];
char f[10];
ll dpmax[1005][10]; //dp[i][j]的含义是,前 i 个数中,选择前j个运算符后得到的最大 K 值
ll dpmin[1005][10];//如 dp[3][2] 的含义是选择前3个数中的两个数和前2个运算符之后得到的最大K值
int main(){
int T;
cin >> T;
while(T--){
mem(a,0);
mem(f,0);
for(int i=0;i<1005;i++){
for(int j=0;j<10;j++){
dpmax[i][j]=-INF;
dpmin[i][j]=INF;
}
}
cin >> N >> M >> k;
for(int i=1;i<=N;i++)
cin >> a[i];
for(int j=1;j<=M;j++)
cin >> f[j];
dpmax[0][0]=dpmin[0][0]=k;
ll ans=-INF;
for(int i=1;i<=N;i++){
dpmax[i][0]=k;
dpmin[i][0]=k;
for(int j=1;j<=min(i,M);j++){
ll maxn,minn;
maxn=dpmax[i-1][j-1];
minn=dpmin[i-1][j-1];
if(f[j]=='+'){
maxn+=a[i];
minn+=a[i];
}else if(f[j]=='-'){
maxn-=a[i];
minn-=a[i];
}else if(f[j]=='*'){
maxn*=a[i];
minn*=a[i];
}else if(f[j]=='/'){
maxn/=a[i];
minn/=a[i];
}
if(maxn<minn)
swap(maxn,minn);
dpmax[i][j]=max(dpmax[i-1][j],maxn);//每一个 j 都要和 上一个 i 的 j比较, 也就是运算符相同的情况下,选择不选择当前数字
dpmin[i][j]=min(dpmin[i-1][j],minn);//maxn 的值是选择第i个房间后的结果,dp[i-1][j] 是不选择第i个房间的结果
}
ans=max(ans,dpmax[i][M]);
}
cout << ans <<endl;
}
return 0;
}