Problem D
Buying Coke
Input: Standard Input
Output: Standard Output
Time Limit: 2 Seconds
I often buy Coca-Cola from the vending machine at work. Usually I buy several cokes at once, since my working mates also likes coke. A coke in the vending machine costs 8 Swedish crowns, and the machine accept crowns with the values 1, 5 and 10. As soon as I press the coke button (after having inserted sufficient amount of money), I receive a coke followed by the exchange (if any). The exchange is always given in as few coins as possible (this is uniquely determined by the coin set used). This procedure is repeated until I've bought all the cokes I want. Note that I can pick up the coin exchange and use those coins when buying further cokes.
Now, what is the least number of coins I must insert, given the number of cokes I want to buy and the number of coins I have of each value? Please help me solve this problem while I create some harder problems for you. You may assume that the machine won't run out of coins and that I always have enough coins to buy all the cokes I want.
The first line in the input contains the number of test cases (at most 50). Each case is then given on a line by itself. A test case consists of four integers: C (the number of cokes I want to buy), n1, n5, n10 (the number of coins of value 1, 5 and 10, respectively). The input limits are 1 <=C <= 150, 0 <= n1 <= 500, 0 <= n5 <= 100 and 0 <= n10 <= 50.
For each test case, output a line containing a single integer: the minimum number of coins needed to insert into the vending machine.
3 2 2 1 1 2 1 4 1 20 200 3 0 |
5 3 148
|
这道题我一看到就觉得是贪心,没感觉出来是dp,于是写了一发,提交WA了。难道不能贪心?我坚信这题可以,一定是我忽略什么情况,于是上网查了题解,清一色的记忆化搜索,可惜有一句话吸引了我,那就是一张10元+3张1元,买一瓶可乐找回一张5元,这个我倒没有考虑进去,于是修改代码,这下AC了,还跑出了0.016s的好成绩,后来我也写了一发记忆化搜索版本的,跑了1.412s,这个就是差距啊。
好了,废话不多说,来说说贪心思想吧。
首先肯定先用完10元喽,留着肯定不是最优解,10元有两种花法,10-2和10+3-5,我先全部用前一种,至于为什么不考虑后一种,等会再说。
设n为要买的可乐数,a,b,c分别为1元,5元,10元的数量。
if(c>=n) 全用10元买
else {
先用光10元,剩下的考虑5元买
n-=c;//n为剩余要买可乐数
if(b>=2*n) 全部用5元买
else if(b<n){
采用5元搭配3张1元的方式买;
因为如果采用2张5元买,那么2瓶可乐得2张5元+8张1元>=2*(1张5元+3张1元);
注意b<n,必然得有8张1元买可乐,这个会最优吗?
此时就要用到之前的10元,10+3找回一张5元,再加3张一元买1瓶可乐
这样相当于塞7次硬币买了1瓶可乐,比直接塞8个1元更优。
所以考虑10数量
n-=b;//n为剩余要买可乐数
if(c>=n) 全部采用塞7次的方式
else 一部分采用塞7次的方式,另外一部分采用8张1元购买
}
else{
一部分采用2张5元,另外一部分采用1张5元+3张1元
但没有8张1元的方式
至于有多少瓶可乐采用2张5元,很显然是b-n;
}
}
可能还会有有点疑问,就是1元钱可能不够,因此某时刻只能用10元和5元呢?也就是10+3获得5元是否一定可行?这个情况应该是不可能出现的。因为这种做法是优于8个1元买可乐的,可能这8个1元是10元找的,起码4张10元吧,用3张10元找回6个1元,之后10+3找回一张5再加3买。显然只要出现1次8个1元,必然出现4张10元。
严格也很难证明了。
贪心代码:
#include<cstdio> #include<iostream> using namespace std; int main() { int t,n,a,b,c; scanf("%d",&t); while(t--){ scanf("%d%d%d%d",&n,&a,&b,&c); int ans=0; if(c>=n) ans+=n; else{ n-=c; ans+=c; if(b<n){ ans+=b*4; n-=b; if(c>=n) ans+=n*7; else ans+=8*n-c; } else if(b>=2*n) ans+=2*n; else ans+=6*n-2*b; } printf("%d\n",ans); } return 0; }
记忆化搜索:
#include<cstdio> #include<iostream> #include<cstring> using namespace std; int remain,dp[710][160][110]; int dfs(int a,int b,int c){ if(a+5*b+10*c==remain) return 0; int &ans=dp[a][b][c]; if(ans) return ans; ans=1<<30; if(c>0) ans=min(ans,dfs(a+2,b,c-1)+1); if(c>0&&a>2) ans=min(ans,dfs(a-3,b+1,c-1)+4); if(b>1) ans=min(ans,dfs(a+2,b-2,c)+2); if(b>0&&a>2) ans=min(ans,dfs(a-3,b-1,c)+4); if(a>7) ans=min(ans,dfs(a-8,b,c)+8); return ans; } int main() { int t,n,a,b,c; scanf("%d",&t); while(t--){ scanf("%d%d%d%d",&n,&a,&b,&c); remain=a+5*b+10*c-8*n; memset(dp,0,sizeof dp); printf("%d\n",dfs(a,b,c)); } return 0; }