头条19提前批算法笔试第三题

题意如下(改编):

海绵宝宝和派大星分吃一堆蟹黄堡,每个蟹黄堡有两种属性值,分别是饱感和满足感,海绵宝宝和派大星需要吃得完全一样饱,同时两人满足感之和最大。假设单个蟹黄堡不可以拆分。

输入:

第一行,一个整数N,表示蟹黄堡的个数;

第二行开始的N行,两个整数,以空格隔开,第一个整数代表饱感,第二个整数代表满足感;

输出:

整数,最大的满足感。

测试用例:

输入:

5

4 4

3 3

2 2

1 1

1 1

输出:

10

拿到题目的第一感觉就是背包问题的变形了,然后开始挠头想状态转移方程,当然现场是没挠出来的。

全部蟹黄堡用两个数组int x[N],int y[N]表示。

我们用dp[i][j]表示前i个蟹黄堡分成两摞,饱感差值为j时,满足感总和的最大值;对于第i+1个蟹黄堡,可以有如下三种分法:

  1. 给派大星
  2. 给海绵宝宝
  3. 谁也不给

对应的状态转移方程就比较明显了dp[i+1][j]=max(dp[i][j],dp[i][j-x[i]]+y[i],dp[i][j+x[i]]+y[i]);

注意以上状态转移方程有一点瑕疵,当饱感差值j小于当前蟹黄堡的饱感x[i]时,你分给饱感更小的那个人使得饱感差值缩小为负了。此时可以考虑交换派大星和海绵宝宝的所有堡,这样两人的满足感总和不变,但两人的饱感之差为正值,使得j的范围集中到0--Σx[i]。

相应的状态转移方程:dp[i+1][j]=max(dp[i][j],dp[i][abs(j-x[i])]+y[i],dp[i][j+x[i]]+y[i]);

再来说说边界条件,当i=0时,j有唯一解0,所以dp[0][0]初始化为0,其余值均初始化为-INF,这个跟完全背包问题中恰好装满整个背包的初始化一致。一般大佬们INF取0x3f3f3f3f,原因是-INF再减去一个INF不越界(32位int),减去其他小于INF的正数也不越界,再有这个值是方便用memset初始化的,因为其每个字节都是0x3f,对一段内存初始化方法为0x3f3f3f3f的方法是memset(a,0x3f,sizeof(a))。

代码如下:

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<iostream>
#define sizen 101
#define size 100100
#define INF 0x3f3f3f3f
using namespace std;
int n;
int x[sizen];
int y[sizen];
int dp[2][size];
void Update(int value, int now, int score) {
	int j = value;
	if (value < 0) {
		j = -j;
	}
//比较dp[i+1][|j-x[i]|]与dp[i][j]和dp[i+1][j+x[i]]与dp[i][j]
	dp[now][j] = max(dp[now][j], score);
}
int Solve() {
	//cout << "#" << endl;
	int now = 0, pre = 1;
	
	for (int j = 0; j < size; j++) {
		dp[now][j] = -INF;
	}
	dp[now][0] = 0;
	for (int i = 0; i < n; i++) {
		swap(now, pre);//滚动数组
		
		for (int k = 0; k < size; k++) {
			dp[now][k] = -INF;
		}
		
		for (int k = 0; k < size; k++) {
			//cout << dp[now][k] << " " << dp[pre][k] << endl;
			if (dp[pre][k] == -INF) {
				continue;
			}
			dp[now][k] = max(dp[now][k], dp[pre][k]);//比较dp[i+1][j]与dp[i][j]
			Update(k + x[i], now, dp[pre][k] + y[i]);
			Update(k - x[i], now, dp[pre][k] + y[i]);
		}
		//cout << endl;
	}
	return dp[now][0];
}
int main() {
	while (scanf_s("%d", &n)) {
		for (int i = 0; i < n; i++) {
			scanf_s("%d%d", &x[i], &y[i]);
		}
		printf("%d\n", Solve());
	}
	return 0;
}

本来是一个二维动态规划问题,空间复杂度O(N*Σx[i]),但是OJ系统还对这点空间做了限制,所以要想办法对空间做优化,这里用的是动态规划里常用的滚动数组法,注意到i+1时刻的状态只与i时刻的状态有关,所以实际上只需要滚动保存这两个状态下的两个一维数组,即用i时刻的状态更新i+1时刻的状态,然后将i+1时刻的状态转存到i时刻,进而更新得到i+2时刻的状态,依此递推,优化后的空间复杂度是O(2*Σx[i])。

转载于:https://my.oschina.net/achieveyj/blog/1928852

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值