饭卡
Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 37289 Accepted Submission(s): 12803
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
Source
UESTC 6th Programming Contest Online
V1【错误】 TLE快排太慢
1
#include <stdio.h>
#include <stdlib.h>
void sot(int a[],int l,int r)
{
int i=l,j=r,k=a[i];
if(i>=j)return ;
while(i<j)
{
while(i<j&&k<=a[j])j--;
a[i]=a[j];
while(i<j&&a[i]<=k)i++;
a[j]=a[i];
a[i]=k;
sot(a,l,i-1);
sot(a,i+1,r);
}
}
int main()
{
int n,i,m,v[1005],dp[1100],j;
while(scanf("%d",&n)!=EOF&&n!=0)
{
for(i=1; i<=n; i++)
scanf("%d",&v[i]);
sot(v,1,n);
scanf("%d",&m);
for(i=0;i<1100;i++)
dp[i]=m;
for(i=1; i<n; i++)
{
for(j=m; j>=5; j--)
{
if(j-v[i]>=5)
dp[j]=dp[j]<dp[j-v[i]]-v[i]?dp[j]:dp[j-v[i]]-v[i];
else
dp[j]=dp[j];
}
}
if(m<5)
printf("%d\n",m);
else
printf("%d\n",dp[m]-v[n]);
}
return 0;
}
V2【正确】(就是把V1改成用c++ stl sort排序做)
从网上查了一下,sort函数用的introsort算法:
1. 快排
2. 递归深度过深时切成堆排
3. partition小于一定阈值时切成插入排序
#include<stdio.h>
#include<algorithm>
using namespace std;
int main()
{
int n,i,m,a[1005],dp[1005],j;
while(scanf("%d",&n)!=EOF&&n!=0)
{
for(i=1; i<=n; i++)
scanf("%d",&a[i]);
sort(a+1,a+n+1);
scanf("%d",&m);
for(i=0;i<=1003;i++)
dp[i]=m;
for(i=1; i<n; i++)//这里一开始我写成了i<=n,把最大的算进去了肯定就错了
{
for(j=m; j>=5; j--)
if(j-a[i]>=5)
dp[j]=min(dp[j-a[i]]-a[i],dp[j]);
}
if(m<5)
printf("%d\n",m);
else
printf("%d\n",dp[m]-a[n]);//最大的直接在这里剪掉
}
}
//这里一开始我写成了i<=n,把最大的算进去了肯定就错了
{
for(j=m; j>=5; j--)
if(j-a[i]>=5)
dp[j]=min(dp[j-a[i]]-a[i],dp[j]);
}
if(m<5)
printf("%d\n",m);
else
printf("%d\n",dp[m]-a[n]);//最大的直接在这里剪掉
}
}
V3【正确】
用c怎么了QAQ,c也是可以的!因为排序不是必须的,我们要的只是最大值,于是
#include <stdio.h>
#include <stdlib.h>
int min(int a,int b)
{
if(a>b)return b;
else return a;
}
int main()
{
int n,i,dp[1005],a[1005],m,j,max,remax;
while(scanf("%d",&n)!=EOF&&n)
{
max=0;//一开始没置零,wa了好几回儿
for(i=1; i<=n; i++)
{
scanf("%d",&a[i]);
if(a[i]>max)
{
max=a[i];
remax=i;
}
}
scanf("%d",&m);
if(m<5)printf("%d\n",m);//这个位置比较好
else
{
for(i=0; i<=1003; i++)
dp[i]=m;
for(i=1; i<=n; i++)
{
if(i!=remax)
{
for(j=m; j>=5; j--)
if(j-a[i]>=5)
dp[j]=min(dp[j-a[i]]-a[i],dp[j]);
}
}
printf("%d\n",dp[m]-max);
}
}
return 0;
}
一开始wa的时候很懵 然后敲了顺序的,也算是巩固一下,就是V4版本了。顺便贴出来
V4【正确】
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int maxx(int a,int b)
{
if(a<b)return b;
else return a;
}
int main()
{
int n,i,dp[1005],a[1005],m,j,max,remax;
while(scanf("%d",&n)!=EOF&&n)
{
max=0;
for(i=1; i<=n; i++)
{
scanf("%d",&a[i]);
if(a[i]>max)
{
max=a[i];
remax=i;
}
}
scanf("%d",&m);
memset(dp,0,sizeof(dp));
if(m<5)printf("%d\n",m);
else
{
for(i=1; i<=n; i++)
{
for(j=m; j>=0; j--)
{
if(i!=remax&&j>=a[i])
dp[j]=maxx(dp[j],dp[j-a[i]]+a[i]);
}
}
printf("%d\n",m-dp[m-5]-max);
}
}
return 0;
}