题目链接
https://ac.nowcoder.com/acm/contest/11176/C
题意
取钞机有n种面值的纸币(无穷张),第一种始终为1.会优先给你大的,给出q个询问,最多取k块钱情况下,最多拿多少张钱。
思路
我们定义cost[i]为不使用i+1的纸币时取多少钱答案最大,gain[i]为此时对应的钱。
显然cost[1]=gain[1]=a[2]-1.
我们考虑递推。
当我们用两种钱时,我们得到的钱是两部分,由1支付的和由2支付的。为了不获得第三种纸币,我们最多拿a[3]以下的钱,为了获得1支付的最大值gain[1],我们在获得2支付的钱后,需要剩下cost[1]的钱,那么也就是可以贪心的拿出a[3]-1-cost[1]的钱获得2支付。我们把这部分钱除以a[2],得到的值t就是能获得t张a[2]。那么gain[2]=gain[1]+t,cost[2]=cost[1]+t*a[2]。就很容易写出递推式了。
之后对于每个询问k,我们二分查找出比他小的第一个cost的值和它对应的下标pos。对于k和cost[pos]的差值部分,我们用pos+1号纸币支付,就可以得到最终花费和最终得到的钱币数了。注意一些实现细节就可以了。
代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
#define int long long
//#define double long double
using namespace std;
typedef long long ll;
const int maxn=2000005;
const int inf=0x3f3f3f3f3f3f3f3f;
int n,m,k;
int a[maxn];
int cost[maxn],gain[maxn];
int ans;
signed main(){
IOS
#ifndef ONLINE_JUDGE
freopen("IO\\in.txt","r",stdin);
freopen("IO\\out.txt","w",stdout);
#endif
int tn=1;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
cost[1]=gain[1]=a[2]-1;
for(int i=2;i<n;i++){
int k=(a[i+1]-cost[i-1]-1)/a[i];
cost[i]=k*a[i]+cost[i-1];//只用到i纸币时,也就是不会被i+1号'截胡'时,选多少钱最优
gain[i]=k+gain[i-1];//这时答案是多少
}
cost[n]=inf;
cin>>m;
while(m--){
cin>>k;
int pos=upper_bound(cost+1,cost+1+n,k)-cost;
pos--;
int a1,a2;
a1=((k-cost[pos])/a[pos+1])*a[pos+1]+cost[pos];
a2=((k-cost[pos])/a[pos+1])+gain[pos];//前半部分就是上文说的t
cout<<a1<<' '<<a2<<endl;
}
}