饭卡
Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 7251 Accepted Submission(s): 2458
某天,食堂中有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
这是我做的第一道背包题目 01背包。这道题说 有n种菜,每种菜可以买,可以不买。大于等于5元的时候才可以成交交易。所以我们记住最贵的菜,并多拿出5元来买最贵的菜,假设有m元,这样就剩下了m-5元来尽可能多的在剩下n-1个菜中买菜,这就是赤裸裸的01背包问题了。
要善于思考,善于剖析问题的本质,通过题目 回归到算法的精髓上,切忌为做题而做题。
#include<iostream>
using namespace std;
int p[1010];
int opt[1010];
int ZeroOnePack(int maxi,int money,int n)
{
memset(opt,0,sizeof(opt));
int i,j;
for(i=1;i<=n;i++)
{
if(i!=maxi)
{
for(j=money;j>=p[i];j--)
{
if(opt[j]<opt[j-p[i]]+p[i])
opt[j]=opt[j-p[i]]+p[i];
}
}
}
return money-opt[money];
}
int main()
{
int i,n,m,max,maxi,ans;
while(cin>>n && n)
{
p[0]=0;
max=0;
for(i=1;i<=n;i++)
{
cin>>p[i];
if(p[i]>max)
{
max=p[i];
maxi=i;
}
}
cin>>m;
if(m<5)
cout<<m<<endl;
else
{
ans=ZeroOnePack(maxi,m-5,n)+5-max;
cout<<ans<<endl;
}
}
return 0;
}
后来发现有另一种方法,就是递推,先排序,先把代码贴在这里,日后研究、
#include"stdio.h"
#include"string.h"
#include"stdlib.h"
int cost[1011];
int c1[2000],c2[2000];
int cmp(const void *a,const void *b)
{
return *(int *)a-*(int *)b;
}
int main()
{
int n,m,limit;
int i,l;
int ans;
while(scanf("%d",&n),n)
{
for(i=0;i<n;i++) scanf("%d",&cost[i]);
qsort(cost,n,sizeof(cost[0]),cmp);
scanf("%d",&m);
if(m<5) {printf("%d\n",m);continue;}
limit=m-5;
memset(c1,0,sizeof(c1));
memset(c2,0,sizeof(c2));
for(i=0;i<n;i++)
{
for(l=0;l<=limit;l++) if(c1[l]) c2[l+cost[i]]=1;
c2[cost[i]]=1;
for(l=0;l<=1100;l++) c1[l]=c2[l];
}
ans=0;
for(i=0;i<=1100;i++) if(c2[i]) ans=i;
printf("%d\n",m-ans);
}
return 0;
}