AtCoder Beginner Contest 231 E Minimal payments
E - Minimal payments
题意
有 n 种货币,第一种的面值永远为1,第二种的面值为第一种的整数倍,依次类推到第 n 种。问现在需要支付 X 元,求哪种方案使得支付的货币个数和找回零钱的个数之和最少。
分析
n的数据范围并不大,可以考虑 记忆化搜索 ,因为求最少,那最优解法则是优先选面值大的,剩下需要支付的再用面值小的去凑出来。对于当前这种面值比较大的货币 v[ i ] 和我们需要支付的值 sum,我们可以选择 x = sum / v[ i ] 张,或是x+1张,在这两种中选最小值即可。用 map 将搜索过的状态记录下来,下次需要用到是直接返回即可。如果当前我们可以选到货币为第一种的话,因为第一种货币面值为 1 ,我们直接返回sum即可。
代码
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
const int N = 2e5 + 5;
#define ll long long
vector<ll>v;
ll mp[N], f[N], n;
ll dfs(map<pair<ll,ll>,ll>&dp,vector<ll>&v,ll sum,ll index){
if(index==0)return sum;
if(dp.count(make_pair(index,sum))==1){
return dp[make_pair(index,sum)];
}
ll x=sum/v[index],y=sum%v[index];
ll ans=min(x+dfs(dp,v,y,index-1),x+1+dfs(dp,v,v[index]-y,index-1));
dp[make_pair(index,sum)]=ans;
return ans;
}
void solve(){
vector<ll>v;
ll n,x;cin>>n>>x;
for(int i=0;i<n;i++){
ll x;cin>>x;
v.push_back(x);
}
map<pair<ll,ll>,ll>dp;
cout<<dfs(dp,v,x,n-1)<<endl;
}
int main() {
int t=1;
// cin >> t;
for(int i=1;i<=t;i++) {
solve();
}
}