2018焦作网络预选赛B- Mathematical Curse DP问题

题目链接 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;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值