饭卡
Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 14463 Accepted Submission(s): 5023
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
<span style="font-size:14px;">for( i = 1; i <=n; i++)
for( j = m; j >= price[i]; j--)
dp[j] = __max(dp[j], dp[j-price[i]]+price[i]);</span>
其实二维下dp方程应该是dp[i][j]=max(dp[i-1][j],dp[i-1][j-price[i]]+price[i])
换成一维后,在计算dp[j]时,由于还没更新dp[j]的值,所以此时等号右边的dp[j]代表的其实是dp[i-1][j],而此时我们还需要一个dp[i-1][j-price[i]], 显然,按照刚才是描述,其实我们需要的是一个没有被第i次循环修改过的,
dp[j-price[i]], 要它在当前计算的dp[j]时没有被修改过,那么就是说,得从for (m到1),
因为如果for(1:m),会再计算 dp[j]时,已经计算过dp[j-price[i]],也就是其意义变为 dp【i】[j-price[i]],而不再是dp【i-1】[j-price[i]]
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
#define INF 0x3f3f3f3f
int n,m;
int price[1005];
int dp[1005];
/* TAT~~这部分是 递归的写法。。递归和递推复杂度好像是一样的。但是由于递归自身的问题 效率比较低,但是比较好理解。本题递归300+,递推 15MS
// 当然要数组得dp[maxn][maxn]
int res;
if (i==1)
{
if (j>=price[i])
res=price[i];
else
res=0;
}
else
if (j>=price[i])
res=__max(rec(i-1,j),rec(i-1,j-price[i])+price[i] );
else
res=rec(i-1,j);
return dp[i][j]=res;
}
*/
int main()
{
while(scanf("%d",&n)!=EOF)
{
int i,j;
if(!n) break;
for (i=1;i<=n;i++)
{
scanf("%d",&price[i]);
}
sort(price+1,price+n+1);
scanf("%d",&m);
if(n==1)
{
printf("%d\n",m-price[1]);
continue;
}
n--;
if (m<5)
{
printf("%d\n",m);
continue;
}
m-=5;
memset(dp,0,sizeof(dp));
for( i = 1; i <=n; i++)
for( j = m; j >= price[i]; j--)
dp[j] = __max(dp[j], dp[j-price[i]]+price[i]);
///j为什么要逆序见上面注解
printf("%d\n", m-dp[m]+5-price[n+1] );
}
return 0;
}