MENU

HDU-2546 饭卡 题解 | ACM

March 10, 2019 • 我爱学习

首先这是一道典型的01背包问题

题目描述电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额。如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够)。所以大家都希望尽量使卡上的余额最少。
某天,食堂中有n种菜出售,每种菜可购买一次。已知每种菜的价格以及卡上的余额,问最少可使卡上的余额为多少。
输入数据多组数据。对于每组数据:
第一行为正整数n,表示菜的数量。n<=1000。
第二行包括n个正整数,表示每种菜的价格。价格不超过50。
第三行包括一个正整数m,表示卡上的余额。m<=1000。

n=0表示数据结束。
数据输出对于每组输入,输出一行,包含一个整数,表示卡上可能的最小余额。
样例1
50
5
10
1 2 3 2 1 1 2 3 2 1
50
0
样例输出-45
32

首先放出AC代码:

#include<stdio.h>
#include<math.h>

int max(int a,int b);
void px(int *a);

int main()
{
    int card,n;
    while(~scanf("%d",&n),n)
    {
    int dishes[1012] = {0};
    int zeroOne[1012] = {0};
        for(int i = 0;i < n;i++)
        {
            scanf("%d",&dishes[i]);
        }
        scanf("%d",&card);
        if(card < 5)
        {
            printf("%d\n",card);
            continue;
        }
        card -= 5;
        px(dishes);


          for(int i = 0; i < n-1 ;i++)
        {

            if(dishes[i] == 0)
                break;
            for(int j = card;j >= dishes[i];j--)
            {
                zeroOne[j] = max(zeroOne[j],zeroOne[j-dishes[i]]+dishes[i]);
            }
        }
        printf("%d\n",(card+5-zeroOne[card]-dishes[n-1]));
    }

    return 0;
}

void px(int *a)
{
    int i,j;
    int temp;
    for(i = 0;i < 1012;i++)
    {
        if(a[i]==0)
            continue;
        temp = a[i];
        j = i-1;
        while(j>=0&&a[j] > temp)
        {
            a[j+1] = a[j];
            j--;
        }
        a[j+1] = temp;
    }
}

int max(int a,int b)
{
        return a>b?a:b;

}

我认为此题的关键在于以下几点:

  • 01背包算法
  • 卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负)否则无法购买(即使金额足够)

所以在代码里面,我将输入情况分为金额少于5元和多于5元两种情况。

  • 金额少于5元:
if(card < 5)
{
    printf("%d\n",card);
    continue;
}
  • 金额多于5元
card -= 5;
px(dishes);


for(int i = 0; i < n-1 ;i++)
{

    if(dishes[i] == 0)
        break;
    for(int j = card;j >= dishes[i];j--)
    {
        zeroOne[j] = max(zeroOne[j],zeroOne[j-dishes[i]]+dishes[i]);
    }
}

当金额多于5元时,先将这5元保留,以便最后去买最贵的菜。
用减去5元之后的余下金额去做01背包
在这里,我最一开始按照我查资料得到的二维数组的方法,写出如下代码(因为存在仍然理解不透彻的原因,可能有错):

for(int i = 0;i < count;i++ )
{
    for(int j = 0;j <= card;j++)
    {
        zeroOne[i][j] = max(zeroOne[i-1][j],zeroOne[i][j-dishes[i-1]]+dishes[i-1])
    }
}

由于还是理解不透彻,于是上网查找有关本题的滚动数组优化代码:

for(int j = card;j >= dishes[i];j--)
{
    zeroOne[j] = max(zeroOne[j],zeroOne[j-dishes[i]]+dishes[i]);
}

我觉得这个好想要更好理解一点?
其中变量zeroOne中存放的是已经花掉的金额

最后输出的时候,最终结果为:卡里面最初的总金额 - 已经花掉的金额 - 最贵那道菜的金额。

printf("%d\n",(card+5-zeroOne[card]-dishes[n-1]));
Last Modified: September 8, 2021
Leave a Comment

8 Comments
  1. test

  2. 话说你说css报错是咋回事,我买了你的主题,并且测试是成功的@(小乖) https://www.zrahh.com/usr/uploads/2019/03/2253467355.png

    1. @左岸好的那我再试试,麻烦大佬了,为了帮我试这个还买了个主题

    2. @fenglinger哈哈哈,不得不说这是个缘分,你来我博客看看@(滑稽)

    3. @左岸哇这么巧的吗,很棒哈哈哈哈哈

    4. @左岸终于有时间折腾一下网站了,找到要在哪里插了,但是它的评论功能被封装在一个方法中,很烦

    5. @fenglinger没有这么麻烦呀,Comments.php这样配置就好 https://www.zrahh.com/usr/uploads/2019/03/4244782429.jpg

    6. @左岸哦哦好了好了,之前我还是找错php代码块了,现在就加上了,因为不会php每次看代码都是看名字猜功能 ̄﹃ ̄