题意如下(改编):
海绵宝宝和派大星分吃一堆蟹黄堡,每个蟹黄堡有两种属性值,分别是饱感和满足感,海绵宝宝和派大星需要吃得完全一样饱,同时两人满足感之和最大。假设单个蟹黄堡不可以拆分。 输入: 第一行,一个整数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个蟹黄堡,可以有如下三种分法:
- 给派大星
- 给海绵宝宝
- 谁也不给
对应的状态转移方程就比较明显了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])。