[bzoj3312][Usaco2013 Nov][DP]No Change不找零

来源:互联网 发布:软件成本估算专家 编辑:程序博客网 时间:2024/06/06 17:07

Description

Farmer John is at the market to purchase supplies for his farm. He has
in his pocket K coins (1 <= K <= 16), each with value in the range
1..100,000,000. FJ would like to make a sequence of N purchases (1 <= N <= 100,000), where the ith purchase costs c(i) units of money (1 <=
c(i) <= 10,000). As he makes this sequence of purchases, he can
periodically stop and pay, with a single coin, for all the purchases
made since his last payment (of course, the single coin he uses must
be large enough to pay for all of these). Unfortunately, the vendors
at the market are completely out of change, so whenever FJ uses a coin
that is larger than the amount of money he owes, he sadly receives no
changes in return! Please compute the maximum amount of money FJ can
end up with after making his N purchases in sequence. Output -1 if it
is impossible for FJ to make all of his purchases.

K个硬币,要买N个物品。

给定买的顺序,即按顺序必须是一路买过去,当选定买的东西物品序列后,付出钱后,货主是不会找零钱的。现希望买完所需要的东西后,留下的钱越多越好,如果不能完成购买任务,输出-1

Input

Line 1: Two integers, K and N.

  • Lines 2..1+K: Each line contains the amount of money of one of FJ’s coins.

  • Lines 2+K..1+N+K: These N lines contain the costs of FJ’s intended purchases.

Output

  • Line 1: The maximum amount of money FJ can end up with, or -1 if FJ cannot complete all of his purchases.

Sample Input

3 6
12
15
1
6
3
3
2
3
7

INPUT DETAILS: FJ has 3 coins of values 12, 15, and 10. He must make
purchases in sequence of value 6, 3, 3, 2, 3, and 7.

Sample Output

12

OUTPUT DETAILS: FJ spends his 10-unit coin on the first two purchases,
then the 15-unit coin on the remaining purchases. This leaves him with
the 12-unit coin.

题解

一开始想的是压三维,f[i][j][k]表示第i张卡付j~k的账单
结果发现这会MLE
再看看数据,发现有个神奇的地方。卡不超过16张
那就可以压卡的状态啦!
f[i]表示第i种状态所能付到的最远的账单,预处理一下每张卡能从每个账单开始连续付的数量。之后瞎转移就好
输出的话,遍历每种能付的账单等于总帐单的状态。然后记录一下最小值就好

#include<cstdio>#include<cstring>#include<cstdlib>#include<algorithm>#include<cmath>using namespace std;typedef long long LL;int f[140000];//使用i这些卡最远能付到的账单int m[20][110000];int v[20],w[110000],bin[25];int n,k;LL ret; int main(){    scanf("%d%d",&n,&k);ret=0;    bin[1]=1;for(int i=2;i<=n;i++)bin[i]=bin[i-1]*2;    for(int i=1;i<=n;i++){scanf("%d",&v[i]);ret+=v[i];}    for(int i=1;i<=k;i++)scanf("%d",&w[i]);    for(int i=1;i<=n;i++)    {        int cnt=0,t;        t=1;        while(cnt+w[t]<=v[i] && t<=k)cnt+=w[t++];        t--;        m[i][1]=t;        for(int j=2;j<=k;j++)        {            cnt-=w[j-1];            while(cnt>v[i])cnt-=w[t--];            t++;            while(cnt+w[t]<=v[i] && t<=k)cnt+=w[t++];            t--;            m[i][j]=t-j+1;        }    }    int ans=999999999;    for(int i=1;i<=n;i++)f[bin[i]]=m[i][1];    for(int i=1;i<=bin[n]*2-1;i++)    {        for(int j=1;j<=n;j++)        {            if((i&bin[j])==0)f[i|bin[j]]=max(f[i|bin[j]],f[i]+m[j][f[i]+1]);        }    }    bool bk=false;    for(int i=1;i<=bin[n]*2-1;i++)    {        if(f[i]>=k)        {            bk=true;            int cnt=0;            for(int j=1;j<=n;j++)if((i&bin[j])>0)cnt+=v[j];            ans=min(ans,cnt);        }    }    if(bk==false)printf("-1\n");    else printf("%d\n",ret-ans);    return 0;}
原创粉丝点击