很厉害的题目,没看题解之前一直忽略了题目中的一个性质,下一个硬币面额总是上一个硬币面额的倍数。我也不清楚如果看到了这一点之后我会不会想出答案。
这道题目最优策略要考虑两个因素:
1.取得相同面额时,硬币数量越少越好
2.相同硬币数量时,面额越少越好(首先总面额要大于C)
这个限制条件是很难满足的,我自己想的一些贪心策略都可以举反例证伪(举得反例有一些是不符合倍数要求的)。
但是在题目特殊的倍数要求下,这个条件就变得可以满足了。
贪心策略如下,证明本人目前还没想出来:
1.先按面值从小到大排序
2.从后往前取,在保证总面额小于等于C的前提下尽量多取
3.从前往后取,直到取得总面额大于等于C
很奇妙的是,这个贪心策略加上题目特殊的倍数要求,就变得总是正确的了,里面的证明肯定很精彩,以后如果我想起来了再补充吧。
代码如下:
#include <map>
#include <queue>
#include <cstdio>
#include <vector>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define MAX_N 10005
using namespace std;
struct data
{
int value;
int number;
bool operator < (const data& b) const
{
return value < b.value;
}
};
typedef long long int ll;
int main()
{
//freopen("in.txt", "r", stdin);
int N, C;
data dat[MAX_N];
while (~scanf("%d%d", &N, &C))
{
for (int i = 0; i < N; i++)
scanf("%d%d", &dat[i].value, &dat[i].number);
sort(dat, dat + N);
ll ans = 0;
for (int i = N - 1; i >= 0; i--)//大于C的硬币先处理
{
if (dat[i].value >= C)
{
ans += dat[i].number;
dat[i].number = 0;
}
else
break;
}
while (1)
{
int need = C;
for (int i = N - 1; i >= 0; i--)
{
if (dat[i].number && need >= dat[i].value)
{
int k = need / dat[i].value;
k = min(k, dat[i].number);
need -= k * dat[i].value;
dat[i].number -= k;
}
}
for (int i = 0; i <= N - 1; i++)
{
if (dat[i].number && need)
{
int k;
if (need % dat[i].value)
k = (need + dat[i].value) / dat[i].value;
else
k = need / dat[i].value;
k = min(k, dat[i].number);
if (need >= k * dat[i].value)
need -= k * dat[i].value;
else
need = 0;
dat[i].number -= k;
}
}
if (need == 0)
ans++;
else
break;
}
printf("%lld\n", ans);
}
return 0;
}