饭卡(背包问题)

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

##此题实在是烧心呀,近三个小时,才算是把01背包问题,动态规划弄懂大概,以后切记要多刷题,总结如下:

1.坑点,注意当m<5时要输出余额。wa了5次。
2.尽可能把余额kongzhi控制在距离5最近的范围,我原本以为此题可以利用sort解题,原因是没有考虑全面,因为sort的解决是有序的一旦使用过的数据就不能再使用了。wa了两次。错误代码如下:

#include<cstdio>
#include<cstring>
#include<queue>
#include<iostream>
#include<string>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<cmath>
#define ll long long
using namespace std;
const int maxn=1010;
int a,b,c,d,sum,va[maxn];
int main(){
    while(scanf("%d",&a)!=EOF&&a){
        memset(va,0,sizeof(va));
        for(int i=0;i<a;i++){
            scanf("%d",&va[i]);
        }
        scanf("%d",&b);
            if(b<5){
            printf("%d\n",b);
            continue;
        }
        sort(va,va+a);
        c=va[a-1];
        for(int i=a-2;i>=0;i--){
            if(b-va[i]>=5){
                b-=va[i];
            }
        }
        printf("%d\n",b-c);
    }
    return 0;
}

想想当时还感觉自己挺聪明的就感觉可笑。
3.笔记一下动态规划中数据分别代表的意义,ac代码如下:

#include<cstdio>
#include<cstring>
#include<queue>
#include<iostream>
#include<string>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<cmath>
#define ll long long
using namespace std;
const int maxn=1200;
int a,b,c,d,sum,va[maxn],dp[maxn][maxn];//用va存饭菜的价格,用dp存动态规划数据
int main(){
    while(scanf("%d",&a)!=EOF&&a){
        memset(va,0,sizeof(va));
        memset(dp,0,sizeof(dp));
        for(int i=0;i<a;i++){
            scanf("%d",&va[i]);
        }
        scanf("%d",&b);
        if(b<5){
            printf("%d\n",b);//坑点
            continue;
        }
        sort(va,va+a);//用或不用都行,我是在原错误代码的基础上做的修改
        c=va[a-1];//用c存最贵的饭菜
        for(int i=1;i<=a-1;i++){//dp第一维度代表第i种菜
            for(int j=b;j>=5;j--){//第二维度代表的是当前的钱数
                if(j-5>=va[i-1]){
                    dp[i][j] =max(dp[i-1][j],dp[i-1][j-va[i-1]] + va[i-1]);
                }
                else
                    dp[i][j]=dp[i-1][j];

            }
        }
        printf("%d\n",b-c-dp[a-1][b]);
    }
    return 0;
}

1.对于动态规划问题,尤其是这种01背包问题,必须明白每个变量所代表的意义。起初我认为dp的第二维度代表的是剩余的钱数,因为毕竟套用的公式也是用j-va[i-1]然而再顺着这个逻辑往下就说不通了。原因就是掉进题目里面了。之后才明白它的真正意义就是此时的钱数。
2.明白了dp个体,就接着理解dp整体代表了什么含义,dp整体代表的就是当钱数为j时的余额最接近5的方案。
3.接着理解整个表达式dp[i][j] =max(dp[i-1][j],dp[i-1][j-va[i-1]] + va[i-1]);代表的意义,这个就是选与不选的问题了,如果未选va[i-1],那么dp[i][j]=dp[i-1][j];,而当选择了va[i-1]时,就需要判断max里面的两个式子谁大谁小的问题了,而第一个式子代表的是未选择va[i-1]是的最优情况,第二个式子代表的是dp[i-1][j-va[i-1]] 这时候的最优情况,再加上va[i-1]的值。

加油!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

旺旺的碎冰冰~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值