打牌第三天之多重背包

多重背包问题
题目:有n种物品和一个容量为v的背包。第i种物品最多有n[i]件可以取,每件体积是c[i],价值是w[i]。求解将哪些物品装进背包可使这些物品的体积总和不超过背包的容量,且价值最大。

解法
1.和完全背包不同的是,这里的每件物品数量是有限制的,所以不能用完全背包的一维解法。将原始完全背包的状态转移改改
f[i][v]=max(f[i][v],f[i][vkc[i]]+kw[i]);0<=k<=n[i]
时间复杂度为 O(vn[i]) ,空间复杂度为 O(vn)
2.转化为01背包。单纯01背包,时间复杂度为 O(vn[i]) ,空间复杂度为 O(n[i]) ,可以优化一下,就是数量转化为二进制。时间复杂度为 O(vlog(n[i])) ,空间复杂度为 O(log(n[i]))
3.时间复杂度为O(v*n),用单调队列优化,参考和下面题目代码来源(稍作改动)http://www.cnblogs.com/ka200812/archive/2012/07/11/2585950.html

   我要长高

Description
韩父有N个儿子,分别是韩一,韩二…韩N。由于韩家演技功底深厚,加上他们间的密切配合,演出获得了巨大成功,票房甚至高达2000万。舟子是名很有威望的公知,可是他表面上两袖清风实则内心阴暗,看到韩家红红火火,嫉妒心遂起,便发微薄调侃韩二们站成一列时身高参差不齐。由于舟子的影响力,随口一句便会造成韩家的巨大损失,具体亏损是这样计算的,韩一,韩二…韩N站成一排,损失即为C*(韩i与韩i+1的高度差(1<=i<N))之和,搞不好连女儿都赔了.韩父苦苦思索,决定给韩子们内增高(注意韩子们变矮是不科学的只能增高或什么也不做),增高1cm是很容易的,可是增高10cm花费就很大了,对任意韩i,增高Hcm的花费是H^2.请你帮助韩父让韩家损失最小。

Input
有若干组数据,一直处理到文件结束。 每组数据第一行为两个整数:韩子数量N(1<=N<=50000)和舟子系数C(1<=C<=100) 接下来N行分别是韩i的高度(1<=hi<=100)。



首先建立方程,很容易想到的是,dp[i][j]表示第 i 个儿子身高为 j 的最低花费。分析题目很容易知道,当前儿子的身高花费只由前一个儿子影响。因此,

dp[i][j]=min(dp[i-1][k] + abs(j-k)*C + (x[i]-j)*(x[i]-j));其中x[i]是第i个儿子原本的身高

我们分析一下复杂度。

首先有N个儿子,这需要一个循环。再者,每个儿子有0100的身高,这也需要一维。再再者,0100的每一个身高都可以有前一位儿子的身高0100递推而来。

所以朴素算法的时间复杂度是O(n*100*100)。题目只给两秒,难以接受!

分析方程:

当第 i 个儿子的身高比第 i-1 个儿子的身高要高时,

dp[i][j]=min(dp[i-1][k] + j*C-k*C + X);   ( k<=j ) 其中 X=(x[i]-j)*(x[i]-j)。

当第 i 个儿子的身高比第 i-1 个儿子的身高要矮时,

dp[i][j]=min(dp[i-1][k] - j*C+k*C + X);   ( k>=j )

对第一个个方程,我们令 f[i-1][k]=dp[i-1][k]-k*C,  g[i][j]=j*C+X; 于是 dp[i][j] = min (f[i-1][k])+ g[i][j]。转化成这样的形式,我们就可以用单调队列进行优化了。

第二个方程同理。

接下来便是如何实现,实现起来有点技巧。具体见下
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>

using namespace std;
#define inf ox3f3f3f3f

int dp[2][101],n,c,q[101],head,tail,cur;

int main()
{
    int i,j,x,nowf;
    while(scanf("%d %d",&n,&c)!=EOF)
    {
        scanf("%d",&x);
        cur=0;
        for(i=0;i<x;i++)
            dp[cur][i]=inf;
        for(i=x;i<=100;i++)
            dp[cur][i]=(x-i)*(x-i);
        for(i=1;i<n;i++)
        {
            scanf("%d",&x);
            cur=1-cur;
            head=tail=0;
            //i tall
            for(j=0;j<=100;j++)//正着写,是因为当前人比前面高,如果当前人的身高是j,需要知道前一个人比他矮的情况
            {
                nowf=dp[1-cur][j]-j*c;
                while(head<tail&&q[tail-1]>nowf)
                    tail--;
                q[tail++]=nowf;
                if(j<x)
                    dp[cur][j]=inf;
                else dp[cur][j]=q[head]+j*c+(x-j)*(x-j);
            }
        }
        //i short
        head=tail=0;
        for(j=100;j>=0;j--)//倒着写,是因为当前人比前面高,如果当前人的身高是j,需要知道前一个人比他矮的情况
        {
            nowf=dp[1-cur][j]+j*c;
            while(head<tail&&q[tail-1]>nowf)
                tail--;
            q[tail]=nowf;
            if(j>=x)
                dp[cur][j]=min(dp[cur][j],q[head]-j*c+(x-j)*(x-j));
        }
    }
    int ans=inf;
    for(int i=0;i<=100;i++)
        ans=min(ans,dp[cur][i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值