1. 程式人生 > >省選專練之[USACO13NOV]沒有找零No Change

省選專練之[USACO13NOV]沒有找零No Change

約翰到商場購物,他的錢包裡有K(1 <= K <= 16)個硬幣,面值的範圍是1…100,000,000。

約翰想按順序買 N個物品(1 <= N <= 100,000),第i個物品需要花費c(i)塊錢,(1 <= c(i) <= 10,000)。

在依次進行的購買N個物品的過程中,約翰可以隨時停下來付款,每次付款只用一個硬幣,支付購買的內容是從上一次支付後開始到現在的這些所有物品(前提是該硬幣足以支付這些物品的費用)。不幸的是,商場的收銀機壞了,如果約翰支付的硬幣面值大於所需的費用,他不會得到任何找零。

請計算出在購買完N個物品後,約翰最多剩下多少錢。如果無法完成購買,輸出-1
記錄F(1<<i)時的最遠購物距離
然後列舉已經更新了什麼刷表法更新。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+100;
inline void read(int &x){
    x=0;
    int f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    x*=f;
}
int S[N]={};
int C[21]={};
int F[(1<<17)];
int n,k;
int Mx;
int Get(int now,int Id){
    int sum=S[Id]+C[now];
    if(sum>S[n])return n;
    int pos=upper_bound(S+1,S+1+n,sum)-S-1;
    return max(pos,Id);
}
int main(){
    memset(F,-1,sizeof(F));
    F[0]=0;
    read(k);
    read(n);
    for(int i=1;i<=k;++i){
        read(C[i]);
    }
    for(int i=1;i<=n;++i){
        read(S[i]);
        S[i]+=S[i-1];
    }
    int Mx=(1<<k)-1;
    for(int i=0;i<=Mx;++i){
        if(F[i]==-1)continue;
        for(int j=0;j<k;++j){
            if(!(i&(1<<j))){
                F[i|(1<<j)]=max(F[i|(1<<j)],Get(j+1,F[i]));
            }
        }
    }
    int ans=-1;
    for(int i=0;i<=Mx;++i){
        if(F[i]==n){
            int now=0;
            for(int j=0;j<k;++j){
                if(!(i&(1<<j))){
                    now+=C[j+1];
                }
            }
            ans=max(ans,now);
        }
    }
    cout<<ans;
}