最大购物优惠(二维费用背包求解最小字典序最优方案)

问题 AD: 【基础】最大购物优惠

题目描述

小惠听说超市正在打折促销,要制订一个得到最大优惠的购物计划。

小惠的体力可以提起 w 单位重量的东西,还有一个能装 v 个单位体积的购物袋,并详细了解了各打折商品的重量、体积及此商品实际优惠的金额。她想在自己体力的限度和购物袋容积限度内,尽可能多地得到购物优惠。

超市规定这些打折商品每种只能购买一件。

请你编写程序,制定一个购买商品的计划,求出小惠能得到的最大优惠金额和实际应购买的各商品序号。

输入

第一行:依次为 w、v 和 n(n 为商品种类数),所有数值均为不超过 100 的正整数

接下来的 n 行:每行有三个整数,依次为某种商品的重量、体积和让利金额,数值间以空格分开,所有数值均为不超过 100 的正整数

输出

第一行:小惠能够得到的最大让利金额

第二行:依次为从小到大排列的商品序号,序号从 1 开始,序号间用空格分开。若第二行输出的序列不唯一,则输出其最小字典序。

样例输入

10 9 4
8 3 6
5 4 5
3 7 7
4 5 4

样例输出

9
2 4

分析

这是一个二维费用背包求解最小字典序最优方案的问题,由于是二维费用所有每个状态就是dp[i][w][v]代表前i个物品在还有w空间和v空间的情况下的的最大价值,状态转移就是选和不选第i个物品

dp[i][w][v] = dp[i-1][w][v] //不选择第i个物品,继承第i-1的状态  
dp[i][w][v] = dp[i][w-wight[i]][v-vol[i]] + val[i]  //选择第i个物品,更新状态

但是这道题还需要输出最小字典序最优方案,背包问题循环遍历的时候会继承前一个状态,因此,直接从最后一个状态往前寻找它的前一个状态则是最大字典序的方案,所以我们可以把物品倒序存储,这样每个状态的上一个状态就是最小的物品序号,这样之后,还需要一个一个数组记录取i物品的情况f[i][w][v] = 1代表第i个物品在有w空间和v空间的情况被取了,最后通过回溯f状态输出,但注意这里的i实际上是n-i+1物品,输出也就是n-i+1

代码


int dp[200][200][200];//记录每个状态的价值
int f[200][200][200];//记录每个状态取物品
int wi[200],vi[200],si[200];//记录物品信息
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    //
    // freopen("E:/Code/C++/untitled1/input.txt","r",stdin);
    // freopen("output.txt","w",stdout);


    int w,v,n;
    cin >> w >> v >> n;
    for(int i = 1;i <= n;++i)
    {
        cin >> wi[n-i+1] >> vi[n-i+1] >> si[n-i+1];  //倒序输入物品,使它能够字典序最小
        //de3(wi[n-i+1],vi[n-i+1],si[n-i+1]);
    }

    for(int i = 1;i <= n;++i)
        for(int j = 0;j <= w; ++j) //没个状态都需要继承,不能从wi[i]开始,否则有的情况就不能顺利继承
            for(int k = 0; k <= v; ++k)
            {
                dp[i][j][k] = dp[i-1][j][k];  //继承上一个状态
                if (j >= wi[i] && k >= vi[i] && dp[i][j][k] <= dp[i - 1][j - wi[i]][k - vi[i]] + si[i])  
                {
                    dp[i][j][k] = dp[i-1][j-wi[i]][k-vi[i]] + si[i];  //状态转移
                    f[i][j][k] = 1;  //记录状态
                }
                //de(dp[i][j][k]);
            }
    cout << dp[n][w][v] << '\n';

    for(int i = n;i >= 1;--i)  //回溯, 从最后的状态开始回溯
    {
        if(f[i][w][v])  //如果选择的物品i
        {
            cout << n - i + 1 <<' ';  //物品i实际上是物品n-i+1,满足字典序所换的顺序
            w -= wi[i];  //回溯上一个状态
            v -= vi[i];  //回溯上一个状态
        }
    }



posted @   bakul  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示