其实这道题就按照题面意思直接模拟一下就好喇!
很显然如果给定硬币的最小值大于1则输出"No answer!!!"因为这样子就无法取到1这个面值了。
先证明一下这一点:如果目前状态可取前 x x x 价格,且当前取了一个面额为 a a a 的硬币,要想构成前 x + a x + a x+a 的所有价格,必须仅当 x ≥ a − 1 x \ge a - 1 x≥a−1 时成立—>
证:若 x < a − 1 x < a - 1 x<a−1, 则我们取最小的 x + 1 x + 1 x+1,它必须由 x + 1 − a x + 1 - a x+1−a 得到,而 x < a − 1 x < a - 1 x<a−1,故 x + 1 − a < 0 x + 1 - a < 0 x+1−a<0,由于不存在小于0的状态,故原命题成立。
假设当前状态,前 i i i 种硬币已经用最小的硬币数 a n s ans ans 构成最大的可行价格 t o t tot tot ,那么如果当前 t o t ≥ a i + 1 − 1 tot \ge a_{i + 1} - 1 tot≥ai+1−1,那么就可以直接 i + + i + + i++;否则累加,使 t o t = t o t + a i tot = tot + a_{i} tot=tot+ai 至 t o t ≥ a i + 1 − 1 tot \ge a_{i + 1} - 1 tot≥ai+1−1 , 同时每次 a n s ans ans + + + + + + 。因为我们在每一步都满足 t o t ≥ a i + 1 − 1 tot \ge a_{i + 1} - 1 tot≥ai+1−1 , 所以每次 i + + i++ i++ 时,我们都可以放心操作当前的 t o t + a i tot + a_{i} tot+ai 操作。特别的,当 i i i 等于1时,如果 t o t ( = 0 ) < a 1 − 1 tot(=0) < a_{1} - 1 tot(=0)<a1−1 , 即 a 1 > 1 a_{1} > 1 a1>1时,不能满足条件,因而无答案,这里解释了第二段的显然结论。
关于这个累加,由于数据范围很大,我们很容易被卡(如都是1),所以我们先算出 t o t tot tot 加上多少个 a i a_{i} ai 才能大于等于 a i + 1 a_{i + 1} ai+1,即个数 k = ( a i + 1 − 2 − t o t ) ÷ a i + 1 k = (a_{i + 1} - 2 - tot) \div a{i} + 1 k=(ai+1−2−tot)÷ai+1 ,再 t o t = t o t + k ∗ a i tot = tot + k * a_{i} tot=tot+k∗ai, a n s = a n s + k ans = ans + k ans=ans+k, 用 O(1)的时间解决了问题。同时我们把 a n + 1 a_{n + 1} an+1 赋值为 m m m , 助于判停。
最后注意一点:如果 t o t tot tot 刚好等于 m − 1 m - 1 m−1 ,并结束了循环,此时我们要在循环外输出 a n s + 1 ans + 1 ans+1 , 以应对此特殊情况。
下面我来贴一下代码—>
#include <cstdio>
#include <algorithm>
#define ll long long
#define inf 1023456789
using namespace std;
ll n, m, a[2000005], ans, tot;
int main(){
scanf("%lld%lld",&n,&m);
for(int i = 1; i <= n; i++)
scanf("%d",&a[i]);
sort(a + 1, a + n + 1);
if(a[1] != 1) {
printf("No answer!!!\n");
return 0;
}
a[n + 1] = m;
for(int i = 1; i <= n; i++){
if(tot < a[i + 1] - 1){
ll k = (a[i + 1] - 2 - tot) / a[i] + 1;
tot += a[i] * k;
ans += k;
if(tot >= m){
printf("%lld\n",ans);
return 0;
}
}
}
printf("%lld\n",ans + 1);
return 0;
}