POJ 2184 Cow Exhibition(DP:01背包)
http://poj.org/problem?id=2184
题目:每头牛有一个S值和F值,现在要求你在N头牛里面选牛,使得被选牛的S值总和SS ,加上F值总和FF,即SS+FF最大.并且SS>=0且FF>=0.问你那个最大值是多少?
分析:二维价值最大问题转化为一维价值最大问题.
现在暂且加上所有S值都为正数,那么我们令dp[i][s]=x表示处理完前i个牛后,当前被选的牛S值和为s时,其F值总和最大为x.
dp[i][s]= max( dp[i-1][s] , dp[i-1][s-Si]+Fi ),s>=Si,则当求完所有的dp[n][j]之后, 0<=j<=SS. 就可以在这些dp[n][j]=x中选出j+x最大的那个值.
现在的问题是S的值有可能为负,这里就要注意了:
假设s=100,Si=10,那么dp[i][100]= max( dp[i-1][100] , dp[i-1][90]+Fi ),所以s依然可以从MAXN循环到10从而更新一维dp的.此时是大值依赖小值.应是s值从大到小更新.
假设s=100,Si=-10,那么dp[i][100]= max( dp[i-1][s] , dp[i-1][100+10]+Fi ),所以此时dp[i-1][110]可能越界,不能这么算了. 所以s应该从小值0循环到MAXN-10,才对.此时是小值依赖大值.应使s值从小到大更新.
又因为Si的和取值范围是[-10W,10W]之间,所以这个需要用一条宏语句解决:#define dp(a) dp[a+100000] 即可.
dp(-20) 映射到了dp[10W-20]上.
初始:除了dp(0)=0外,其他所有dp(i)=-100W,i输入[-10W,10W]内.
AC代码:172ms
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXW = 200000 + 1000;
const int INF = 1000000;
#define dp(i) dp[(i)+100000]
int nKind, dp[MAXW];
int weight[1010],value[1010];
void ZeroOnePack()
{
for(int i = 1; i <= nKind; i++)
if(weight[i] >= 0)
{
for(int j = 100000; j - weight[i] >= -100000; j--)
if(dp(j - weight[i]) != -INF)//有效值
dp(j) = max(dp(j) , dp(j - weight[i]) + value[i]);
}
else
{
for(int j = -100000; j - weight[i] <= 100000; j++)
if(dp(j - weight[i]) != -INF)
dp(j) = max(dp(j) , dp(j - weight[i]) + value[i]);
}
}
int main()
{
while(scanf("%d",&nKind)==1)
{
for(int i=1;i<=nKind;i++)
{
scanf("%d%d",&weight[i],&value[i]);
}
for(int i=-100000;i<=100000;i++) dp(i)=-INF;
dp(0)=0;
ZeroOnePack();
int sum=0;
for(int i=0;i<=100000;i++)
{
if(dp(i)>=0 && i+dp(i)>sum)
sum = i+dp(i);
}
printf("%d\n",sum);
}
}