原题: http://poj.org/problem?id=3040
//题目大意:John每个星期都要给Bessie工资,每个星期的工资>=C,即每个星期最低工资为C,现在John有N种不同面额的硬币,要求出John最多能付给Bessie多少个星期的工资?
//思路 : 将硬币大致分成两类, ①面额小于每个月最低工资的硬币,②面额大于或等于最低工资的硬币
// 1.硬币面额大于最低工资,那就每个星期给一个硬币即可,所以这类的硬币的个数就是 John 所能支付给Bessie的星期数
// 2.硬币面额小于最低工资,那我们就要用它们凑到 >=C,我们凑的过程要尽量避免浪费,方案如下。
// 凑钱方案:对于给定的C,我们先用较大的面额(从大到小遍历)的硬币去凑到最接近C,即小于或等于C,如果不能刚好凑到C,即<C,我们再用面额较小(由小到大遍历)的硬币去凑,直到恰好好比C终止,星期数+1,重复循环,直到最后硬币不够凑到>=C了即可
// 最后把1、2的星期数 相加。
//想了很久,千辛万苦,错了两次,终于AC,激动。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<math.h>
#define qwq 0x7fffffff
using namespace std;
struct Coin
{
int vi;
int ni;
}coin[21];
int cmp(Coin a,Coin b)
{
return a.vi<b.vi;
}
int main()
{
int n,m;
while(~scanf("%d %d",&n,&m))
{
for(int i=0;i<n;i++)
{
scanf("%d %d",&coin[i].vi,&coin[i].ni);
}
sort(coin,coin+n,cmp);
int pos=-1;
for(int i=0;i<n;i++)
{
if(coin[i].vi>=m)
{
pos=i;
break;
}
}
int ans=0;
if(pos!=-1){//找到面额>=m的硬币
for(int i=pos;i<n;i++)
{
ans+=coin[i].ni;
}
}else{//未找到
pos=n;
}
// printf("ans=%d\n",ans);
while(true)
{
int rest=m;
for(int i=pos-1;i>=0;i--) //从大到小遍历,尽量最接近C,但不超过C
{
if(coin[i].ni==0)continue;
if(coin[i].vi>rest)continue;
int ni=coin[i].ni;
int vi=coin[i].vi;
int need=rest/vi;
if(need>coin[i].ni)
{
need=coin[i].ni;
coin[i].ni=0;
}else{
coin[i].ni=coin[i].ni-need;
}
rest=rest-need*vi;
if(rest==0){
ans++;
break;
}
}
if(rest>0) //凑不到等于C,即<C
{
for(int i=0;i<=pos-1;i++)//用面额小的,从小到大去凑直到>C
{
if(coin[i].ni==0)continue;
int ni=coin[i].ni;
int vi=coin[i].vi;
int need=rest/vi;
if(rest%vi)
{
need++;
}
if(need>coin[i].ni)
{
need=coin[i].ni;
rest=rest-need*vi;
coin[i].ni=0;
}else{ //need<coin[i].ni
rest=0;
coin[i].ni=coin[i].ni-need;
}
if(rest==0){
ans++;
break;
}
}
if(rest>0){//凑不到C了,结束
break;
}
}
}
printf("%d\n",ans);
}
return 0;
}