题解:
你有很多张面额不同的纸币,你每个星期要给奶牛至少c元,问你用现在的钱最多给奶牛多少周。
这个题的感觉就是贪心,想了两三种方案感觉都不太对,后来发现这真的是很好的一个题,首先,
将大于等于c的面额的钱直接每个星期给奶牛一张,将面额大于等于c的前去除,然后从大到小开始选择,要选择的金额尽可能的接近c,如果刚好能够凑足c就作为当前的一种方案,如果不能凑足c那就再从小的开始选,要选出一种的总额不少于c但尽量接近c作为当前的方案,然后计算如果按照如此方案最多可以给奶牛多少周,然后按照相同的方法再选方案,一直选到选择的金额不能凑足c为止。
将硬币从小到大排序,从后往前扫描,那种直接能支付的大硬币就直接支付了。对于小硬币,我们一次一次地凑,先从大往小凑,看一周的剩余价值中最多可以使用多少这种硬币,并记录(而且要减去已经凑好的)然后如果还没凑够,就从小向大一个一个地加上,看能不能达到,如果能就记录并开始下一轮的凑,如果不能就退出(此时一定再也不能凑出了,请想一想)
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct node
{
__int64 v,m;
}coin[22];
bool cmp(node a,node b)
{
return a.v > b.v;
}
int main()
{
__int64 n,c;
__int64 ans;
while (~scanf ("%I64d %I64d",&n,&c))
{
for (int i = 0 ; i < n ; i++)
scanf ("%I64d %I64d",&coin[i].v,&coin[i].m);
sort (coin , coin + n , cmp);
ans = 0;
bool flag = true; //是否能继续拿
for (int i = 0 ; i < n ; i++)
{
if (coin[i].v >= c)
ans += coin[i].m;
else //首先从大往小拿
{
while (coin[i].m)
{
int t = 0; //已经拿的钱数
for (int j = i ; j < n ; j++)
{
if (!coin[j].m) //没钱了就跳过
continue;
while (coin[j].m && t < c)
{
t += coin[j].v;
coin[j].m--;
}
if (t == c) //如果正好发完就发了
{
ans++;
break;
}
else //否则就再往下搜
{
t -= coin[j].v;
coin[j].m++;
}
}
if (t == c)
continue;
for (int j = n - 1 ; j >= i ; j--) //不够再从小往大拿
{
if (!coin[j].m)
continue;
if (t + coin[j].m * coin[j].v >= c) //够拿就拿
{
ans++;
coin[j].m -= (c - t + coin[j].v - 1) / coin[j].v;//结果向上取整
//表示的是 (c-t)/coin[j].v;
t=c;//管它后来是多少钱,就按c算
break; // 优化使之不为零
}
else //不够就先拿完
{
t += coin[j].m * coin[j].v;
coin[j].m = 0;
}
}
if (t < c) //如果还不够拿,则无法再发工资
{
flag = false;
break;
}
}
}
if (!flag)
break;
}
printf ("%I64d\n",ans);
}
return 0;
}