蓝桥杯 算法提高-求最大值(dp基础/类01背包+滚动数组)

题目

给n个有序整数对ai bi,

你需要选择一些整数对,使得所有你选定的数的ai+bi的和最大。

要求你选定的数对的ai之和非负,bi之和非负。

1<=n<=100;-1000<=ai,bi<=1000

思路来源

https://blog.csdn.net/qq_39304630/article/details/78570801

http://tieba.baidu.com/p/4362836139

https://paste.ubuntu.com/15331570/ %%%qls

题解

这里和背包很像,一个v[]数组一个w[]数组然后完成转移

dp[i][j],代表前i个数,a[]相加的和为j,b[]相加的和为dp[i][j]

那么dp[i][j]=max(dp[i-1][j],dp[i-1][j-a[i]]+b[i]),

发现只有i和i-1,用滚动数组滚掉就行了

注意到转移的时候,合法的情况

j>=-1000*nj<=1000*n

j+a>=-1000*nj+a<=1000*n

化简得,

max(-1000*n,-1000*n-a)<=j<=min(1000*n,1000*n-a)

然后就是一些小细节的地方,

比如更新最大值,一定要将dp数组初始化为-INF

比如合法的范围,一定在-1000*n到1000*n之间

比如now=0,last=1,滚动数组互换的技巧

由于a数组和非负,只能j从0到1000*n里取dp值

由于b数组和非负,只能取dp[now][j+maxn]非负的值

dp还是太菜了,题解恁地事无巨细

心得

maxn在这里当偏移量用,把负的挪到正的

顺便值得一提,偏移量的英语是offset

看了qls的代码之后还是意识到自己的很多不足

滚动数组可以写的只有now和last,二者互换

之前都是按挑战上写什么i&1和(i+1)&1真是太麻烦了

一题dp仨小时,还是太菜了;

看得懂代码,也看得懂思路,就是自己敲不出来

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=1e5;//同时也是偏移量 offset
const int INF=0x3f3f3f3f;
int ans,n,a,b;
int now,last;//滚动数组 
int low,high;
int dp[2][2*maxn+5];
//dp[i][j]=max(dp[i-1][j-a]+b,dp[i-1][j]);
//本次选择 是对第i个数的选择 
//max的都需要初始化最小值 
int main()
{
	scanf("%d",&n);
	now=0;last=1;
	for(int i=0;i<2;++i)
	{
		for(int j=-1000*n;j<=1000*n;++j)
		dp[i][j+maxn]=-INF;
	} 
	dp[now][maxn]=0;
	for(int i=0;i<n;++i)
	{
		scanf("%d%d",&a,&b);
		swap(now,last);//下一轮 
		for(int j=-1000*n;j<=1000*n;++j)
		dp[now][j+maxn]=dp[last][j+maxn]; 
		low=max(-1000*n,-1000*n-a),high=min(1000*n,1000*n-a);
		for(int j=low;j<=high;++j)
		dp[now][j+a+maxn]=max(dp[now][j+a+maxn],dp[last][j+maxn]+b);
	} 
	ans=-INF;
	for(int j=0;j<=1000*n;++j)
	if(dp[now][j+maxn]>=0)ans=max(ans,dp[now][j+maxn]+j);
	printf("%d\n",ans);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值