Cow Exhibition(变形的01背包)

题目链接:http://poj.org/problem?id=2184
该题目的意思:一共有n头牛,si和fi为其聪明度和有趣度,我们需要选择哪些牛参加展览是的两个的和最大,同时保证这两个值都不为负数。
思路
这道题是一道dp题目,其转移方程就是看是否放下这一头牛,因此我们可以使用01背包来解决这个问题,将其聪明度看为是体积,有趣度看为价值,我们需要计算的是当聪明度大于等于0的时候有趣度最大,然后将两个相加,以此来使得相加最大,但是其中有一个困扰是作为体积来看的聪明度会有负值,所以我们需要在程序中使其始终为正来运行,根据聪明度和有趣度的取值范围,我们可以运用坐标系,将聪明度加上1000,使其范围达到0~2000;这样来,以聪明度为体积的dp[]数组便不会出现体积为负数的情况,在我们最后确定的时候,我们以1000为起点来确定,对相当于我们在原本(-1000到1000)时候的取值。
在移动起点值后,又有着两种选择:一种是我们可以直接在聪明度上加上1000,来进行运算,这样的话,在转移方程中,dp不会出现为负数的体积的情况,所以我们就可以统一的进行从后向前的动态规划。但是这个方法的麻烦之处在于我们还需要定义单独的数组来存储每一个dp[]中多加了多少个1000,以此在最后减去多加上的1000,这样会有一些麻烦,代码就不展示了。
第二种方法,是移动起点,但是我们并不直接在数字上面进行加减,而是在聪明值为负数的时候我们的转移方程的更新进行正向更新。
那么这就涉及到了关于在动态数组规划中我们需要保证更新的值对未更新的值不会产生影响。
关于正向和反向更新
当值为正数的时候,代码块:

for(int i=1;i<=n;i++)
	for(int j=maxx;j>=a[i];j--)
		dp[j]=max(dp[j],dp[j-a[i]]+b[i]);

在值为正数时,我们对动态数组进行n次更新,即考虑将第i个元素放入是否会有影响,当我们使用正向更新的时候已经更新了的值是会对我们未更新的值产生影响的,在一维数组上面可能感知不到,我们不妨换成二维的dp[i][j]=max(dp[i-1][j],dp[i-1][j-a[i]]+b[i]);可以通过这个式子来画图理解;
同理,当值为负数的时候我们也只能通过正向更新来使得保持单遍更新的独立性。
完整代码如下:

#include<bits/stdc++.h>
using namespace std;
#define inf 99999999
const int N=200005;
const int mid=100000;
int n;
int wight[105],value[105],dp[N];
int main()
{
    
    
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>wight[i]>>value[i];//智商属性体积,幽默感属性为价值,问题转换为求体积大等于0时的体积、价值总和。
    for(int i=0;i<=N;i++)//初始化dp,因为有负数,所以初始化为一个非常小的负数
        dp[i]=-inf;//一般memset只能赋值0和-1,所以对于0,-1以外的值一般直接用for循环
    dp[mid]=0;//起始点初始化为0
    for(int i=1;i<=n;i++)
    {
    
    //在w[i]正负的时候进行分类是为了保证每一种情况都不会影响为改变dp[]的独立性
        if(wight[i]>0)
        {
    
    
            for(int j=N-1;j>=wight[i];j--)//正数时候的背包
                dp[j]=max(dp[j],dp[j-wight[i]]+value[i]);
        }
        else
        {
    
    
            for(int j=0;j<N+wight[i];j++)//负数时候的背包
                dp[j]=max(dp[j],dp[j-wight[i]]+value[i]);
        }
    }
    int ans=0;
    for(int i=mid;i<=N;i++)//从智商和为0开始
    {
    
    
        if(dp[i]>=0)//情商和大于0
            ans=max(ans,dp[i]+i-mid);//dp数组存的是TF,通过i-mid计算TS
    }
    cout<<ans<<endl;
    return 0;
}

同时在这道题目中是不能使用memset函数来对dp进行初始化的,因为dp函数我们需要赋值的未-99999999,是无法通过memset函数实现的。

猜你喜欢

转载自blog.csdn.net/malloch/article/details/109012266