物品选取(DP)

【问题描述】

  小沐同学确信所有问题都有个多项式时间算法,为了证明,他决定自己去当一次旅行商,在上路之前,小 X 需要挑选一些在路上使用的物品,但他只有一个 能装体积为 m 的背包。显然,背包问题对小沐来说过于简单了,所以他希望你来帮他解决这个问题。

  小沐可以选择的物品有 n样,一共分为甲乙丙三类:
  1.甲类物品的价值随着你分配给他的背包体积变化,它的价值与分配给它的体积满足函数关系式,v(x) = A*x^2-B*x,x表示分配给该物品的体积,为非负整数,A,B是每个甲类物品的两个参数。注意每个体积的甲类物品只有一个。
  2.乙类物品的价值 A和体积 B都是固定的,但是每个乙类物品都有个参数C,表示这个物品可供选择的个数。
  3.丙类物品的价值 A和体积 B也是固定的,但是每个丙类物品可供选择的个数都是无限多个。

  你最终的任务是确定小沐的背包最多能装有多大的价值上路。

【输入格式】

  第一行两个整数 n,m,表示背包物品的个数和背包的体积;
  接下来 n行,每行描述一个物品的信息。第一个整数 x,表示物品的种类:
  若 x 为1表示甲类物品,接下来两个整数 A,B,为A类物品的两个参数;
  若 x 为2表示乙类物品,接下来三个整数 A,B,C。A表示物品的价值,B表示它的体积,C 表示它的个数;
  若 x 为3表示丙类物品,接下来两个整数A,B。A表示它的价值,B表示它的体积。

【输出格式】

  仅一行为一个整数,表示小 X的背包能装的最大价值。

【输入样例】

【样例1】
 1 0
 1 1 1

【样例2】
 4 10
 2 1 2 1
 1 1 2
 3 5 2
 2 200 2 3

【输出样例】

【样例1】
 0

【样例2】
 610

【数据范围】

对于50%的数据,只有乙和丙两类物品;
对于70%的数据,1<=n<=100, 1<=m<=500,0<=A,B,C<=200;
对于100%的数据,1<=n<=100, 1<=m<=2000,0<=A,B,C<=200;

这道题是个大杂烩,融合了几个背包问题,分析状态转移方程时需要分情况讨论。先设f(i,j)为前i个物品选择一些使得体积为j的最大价值。题目给了三类物品,第一类的方程是f(i,j)=max{f(i-1,j),f(i-1)(j-k)+k*k*a-k*b) | 1<=k<=j} ,第二类的方程是f(i,j)=max{f(i-1,j),f(i-1)(j-k*c[j])+k*v[i]) | 0<=k<=min(j/c[j],c)}, 第三类的方程是f(i,j)=max{f(i-1,j),f(i-1)(j-k*c[j])+k*v[i] | 0<=k<=j/c[j]}。边界是f(0,j)=-inf(选0个物体时体积不可能大于0)和f(i,0)=0(选i个物体时体积等于0时价值为0),然后用填表法实现就可以了。最后答案就是f(n,m)。可以用一维滚动数组优化(第一,二类要倒着填)。
我实现方程的时候把循环j的语句直接放在循环i语句后判断语句前,错了。
后来我把循环j的语句拆分放到每一类里,对了。
不懂。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
using namespace std;
const int inf=1000000000;
int n,m,d[202][2003],s[202];
bool vis[202];
//f(i,j)前i个物品选择一些使得体积为j的最大价值 
//若i是jia f(i,j)=max(f(i-1,j),f(i-1)(j-k)+k*k*a-k*b) 1<=k<=j 
//若i是yi f(i,j)=max(f(i-1,j),f(i-1)(j-k*c[j])+k*v[i]) 0<=k<=min(j/c[j],c)
//若i是bing f(i,j)=max(f(i-1,j),f(i-1)(j-k*c[j])+k*v[i]) 0<=k<=j/c[j]
struct jia
{
    int a,b; 
};
jia x[602];
struct yi
{
    int v,c,w; 
};
yi y[602];
struct bing
{
    int v,c;
};
bing z[602];
int main()
{
    //freopen("select.in","r",stdin);
    //freopen("select.out","w",stdout);
    scanf("%d%d",&n,&m);
    int a,b,v,w,c; 
    for(int i=1;i<=n;i++)
    {
        int t;
        scanf("%d",&t);
        s[i]=t;
        if(t==1)
        {
            scanf("%d %d",&a,&b);
            x[i].a=a;
            x[i].b=b;
        }
        if(t==2)
        {
            scanf("%d %d %d",&v,&c,&w);
            y[i].v=v;
            y[i].c=c;
            y[i].w=w;
        }
        if(t==3)
        {
            scanf("%d %d",&v,&c);
            z[i].v=v;
            z[i].c=c;
        }
    }

    for(int i=0;i<=n;i++) d[i][0]=0;
    for(int i=1;i<=m;i++) d[0][i]=-inf;
    memset(vis,0,sizeof(vis));

    for(int i=1;i<=n;i++)
    {
        if(s[i]==1)
        {
            int t=-inf;
            for(int j=0;j<=m;j++)
            {
                for(int k=0;k<=j;k++)
                {
                    if(t<=d[i-1][j-k]+k*k*x[i].a-k*x[i].b)
                    t=d[i-1][j-k]+k*k*x[i].a-k*x[i].b;
                }
                d[i][j]=t;
            }


        }
        if(s[i]==2)
        {   
            for(int j=y[i].c;j<=m;j++)
            {
                int t=-inf;
                for(int k=0;k<=y[i].w&&k*y[i].c<=j;k++)
                {
                    t=max(t,d[i-1][j-k*y[i].c]+k*y[i].v);
                }
                d[i][j]=t;
            }

        }
        if(s[i]==3)
        {
            int t=-inf;
            for(int j=z[i].c;j<=m;j++)
            {
                for(int k=0;z[i].c*k<=j;k++)
                {
                    t=max(t,d[i-1][j-k*z[i].c]+k*z[i].v);
                }
                d[i][j]=t;
            }
        }
    }
    printf("%d\n",d[n][m]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值