链接:http://acm.hdu.edu.cn/showproblem.php?pid=2546

饭卡

Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 8220    Accepted Submission(s): 2808


Problem Description
电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额。如果购买一个商品之前,卡上的剩余金额大于或等于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背包的思想,值是价值变为可达不可达。用dp[j]表示余额为j是否可达,可达为1,否则为0.状态转移方程为 if (dp[j] && j - price[i] >= 5) dp[j-price[i]] = 1;然后记录转移到最小值,最后输出即可。本题有两个地方需要注意:1、遍历余额的顺序要从0到sum,这样转移的时候都是利用前一次的结果,因为sum是初状态和01背包里的0状态一样。 2、price必须事先经过排序,否则前面大的先转移到5以下,后面小的就没办法更新了。(这是抄的)

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
int main()
{
    int n,m,i,j,t;
    int we[1005],f[1005],cost[1005];
    while(scanf("%d",&n)!=EOF&&n!=0)
    {
        memset(f,0,sizeof(f));
        int max=0;
        for(i=0;i<n;i++)
        {
            scanf("%d",&we[i]);
            if(we[i]>max){max=we[i];t=i;}
        }
        scanf("%d",&m);
        //sort(we,we+n);
       // int x=m-5;
        if(m<5){printf("%d\n",m);continue;}
        else
        {

            for(i=0;i<n;i++)
           {
               if(t==i)continue;
               for(j=m;j>=we[i];j--)
               {
                   f[j]=f[j]>f[j-we[i]]+we[i]?f[j]:f[j-we[i]]+we[i];
               }
          }
        printf("%d\n",m-f[m-5]-max);
        }

    }
}