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();
    }
}
posted @ 2021-12-22 19:17  LiAnG24  阅读(178)  评论(0)    收藏  举报