• 【DP专题】——旅行商的背包


    链接:https://www.luogu.org/problemnew/show/P1782

    个人看来,这道题是一道非常好的多重和完全背包练手题。

    题目描述

    小S坚信任何问题都可以在多项式时间内解决,于是他准备亲自去当一回旅行商。在出发之前,他购进了一些物品。这些物品共有n种,第i种体积为Vi,价值为Wi,共有Di件。他的背包体积是C。怎样装才能获得尽量多的收益呢?作为一名大神犇,他轻而易举的解决了这个问题。

    然而,就在他出发前,他又收到了一批奇货。这些货共有m件,第i件的价值Yi与分配的体积Xi之间的关系为:Yi=ai*Xi^2+bi*Xi+ci。这是件好事,但小S却不知道怎么处理了,于是他找到了一位超级神犇(也就是你),请你帮他解决这个问题。

    输入输出格式

    输入格式:

    第一行三个数n,m,C,如题中所述;

    以下n行,每行有三个数Vi,Wi,Di,如题中所述;

    以下m行,每行有三个数ai,bi,ci,如题中所述。

    输出格式:

    仅一行,为最大的价值。

    既然谈到背包,那么我们来把基本的几个背包复习一遍:

    1.01背包:

      不用多说,01背包就是对于一个物品,你选或者不选,选的状态由你选之前的那个状态转移而来。

      伪代码:

    for:(i) 1->n
        for: (j) maxv->v[i]
            f[j]=max ( f[j-v[i]]+w[i] )

    2.完全背包:

      完全背包考虑到每个物品能装无限个,但实质上,和01背包的区别就是,状态转移不但可以从选之前的那个状态转移而来,还可以是选之后的某个状态(这样相当于重复选择),所以第二层循环要正着写:

    for:(i) 1->n
        for: (j) v[i]->maxv
            f[j]=max ( f[j-v[i]]+w[i] )

    3.多重背包:

      多重背包限定了物品数量,所以不能在原来的基础上增加物品,而是另起一个循环,枚举数量。

    for:(i) 1->n
        for: (j) maxv->v[i]
            for:(k) 1->c[i] (k*v[i]<=j)
                f[j]=max ( f[j-k*v[i]]+k*w[i] )

    其余的背包以后再说。其实是懒得写

    所以接下来就有思路了吧!

    但是呢这道题数据有点坑人,我们需要在基础上做一些优化。

    --单调队列:

    考虑用单调队列来优化:

    参见滑动窗口

    我们在维护最值的时候,可以用单调队列优化。

    --读入优化(废话)

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int N=10010;
    int n,m,cool;
    int v[N],w[N],d[N];
    int a[10],b[10],c[10];
    int tietie[N];
    int fu[N],sai[N];
    int main(){
        scanf("%d%d%d",&n,&m,&cool);
        for(int i=1;i<=n;i++){
            scanf("%d%d%d",&v[i],&w[i],&d[i]);
        }
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&a[i],&b[i],&c[i]);
        }
        for(int i=1;i<=n;i++){
            for(int j=0;j<v[i];j++){
                for(int k=0,bu=0,ki=0;k*v[i]+j<=cool;k++){
                    int matsuri=tietie[j+k*v[i]]-k*w[i];
                    while(bu!=ki&&matsuri>fu[ki-1]) ki--;
                    sai[ki]=k,fu[ki++]=matsuri;
                    if(sai[bu]+d[i]<k) bu++;
                    fu[k*v[i]+j]=fu[bu]+k*w[i];
                }
            }
        }
        for(int i=1;i<=m;i++){
            for(int j=cool;j>=0;j--){
                for(int k=0;k<=j;k++){
                    if(tietie[j]<tietie[j-k]+(k*a[i]+b[i])*k+c[i]) tietie[j]=tietie[j-k]+(k*a[i]+b[i])*k+c[i];
                }
            }
        }
        printf("%d",tietie[cool]);
        return 0;
    } 

    奇怪的是没有过,所以暂时放这了,以后再改。

     

    ——抓住了时间,却不会利用的人,终究也逃不过失败的命运。
  • 相关阅读:
    android: 记录及回复lisView的位置
    android获取屏幕尺寸、密度
    iphone:蓝牙传输
    android 线程 进程
    android 首次使用app时的使用教程的功能的实现
    android 启动界面
    iphone:数组的反序
    android:onKeyDown
    iphone: 可编辑的tableView Move&Delete
    iphone:类似path的抽屉式导航效果的demo总结
  • 原文地址:https://www.cnblogs.com/Nelson992770019/p/11240495.html
Copyright © 2020-2023  润新知