読者です 読者をやめる 読者になる 読者になる

AOJ1028:ICPC: Ideal Coin Payment and Change

太郎くんがP円の物を買う。持っている1円〜500円までの硬貨の枚数が与えられた時、支払う枚数+お釣りの枚数の最小値を求める問題。お釣りは返す枚数が一番少なくなるように返される。

支払う枚数とお釣りの枚数を別々に求める。お釣りの方は貪欲に支払われるので簡単。問題は支払うときの方。
dp[i][j]:i種類目までの硬貨でj円を払う最小の硬貨の枚数
と定義してあげて、(vは硬貨の価値)
dp[i+1][j]=min(dp[i][j-k✕v[i] | 0≦k≦N[i]かつj-k✕v[i]≧0)
である。しかし、これだとN≦1000より間に合わない。ここで、蟻本にも載っている「スライド最小値」を用いる。
b[j]=dp[i][j✕v[i]+a]-j と定義すると、(0≦a<v[i])
dp[i+1][j✕v[i]+a]=min(b[k] | j-N[i]≦k≦j) +j
と表すことが出来る。このminの部分にdequeを用いることで間に合う。

#include <bits/stdc++.h>
using namespace std;
const int kou[]={1,5,10,50,100,500};
int dp[7][700000];
int main(){
    while(true){
        int p;  cin>>p;
        if(p==0)    return 0;
        vector<int> n(6);
        for(int i=0;i<6;i++){
            cin>>n[i];
        }
        for(int i=0;i<7;i++){
            dp[i][0]=0;
            for(int j=1;j<700000;j++)    dp[i][j]=1<<25;
        }
        int end=0;
        for(int i=0;i<6;i++){
            end+=n[i]*kou[i];
            for(int a=0;a<kou[i];a++){
                deque<pair<int,int>> dq;//値、インデックス
                for(int j=0;j*kou[i]+a<=end;j++){
                    int val=dp[i][j*kou[i]+a]-j;
                    while(!dq.empty()&&dq.back().first>=val) dq.pop_back();
                    dq.push_back(make_pair(val,j));
                    dp[i+1][j*kou[i]+a]=dq.front().first+j;
                    while(!dq.empty()&&j-dq.front().second>=n[i])    dq.pop_front();
                }
            }
        }
        int ans=INT_MAX;
        for(int i=p;i<=end;i++){
            int tmp=dp[6][i],v=i-p;
            int tmp2=0;
            for(int j=5;j>=0;j--){
                tmp2+=v/kou[j]; v%=kou[j];
            }
            ans=min(ans,tmp+tmp2);
        }
        cout<<ans<<endl;
    }
}

しばらくはこんな感じでAOJを解き進めつつ、TopCoderCodeforcesに出て行きたいかな、と思います。