[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

输入输出格式

输入格式:
  • 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.

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

输入输出样例

输入样例#1:

3 6
12
15
10
6
3
3
2
3
7

输出样例#1: 复制

12

说明

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.

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.

题目大意:

给出k个硬币,n个商品,可以调整使用硬币的顺序,依次购买商品,每次不会找零,求最后剩下的最大钱数,如不能买全,输出-1

因为\(k\leq16\),考虑状压

\(dp_{sta}\)表示状态为\(sta\)时(二进制,表示哪些硬币使用了,哪些没使用),从一号物品开始能购买的物品数量

显然有

当使用当前状态中未使用的一个硬币时

\(dp[new\_sta]=max(dp[new\_sta],k)\)

其中\(new\_sta\)是转移后的新状态

\(sum_k\)表示前\(i\)个物品的总售价

则k为\(sum_k\)能够支付起是的最大物品数量(最优性)

二分一下即可求出k

代码:

#include<bits/stdc++.h>
#define ll long long
#define INF 2147483647
#define mem(i,j) memset(i,j,sizeof(i))
#define F(i,j,n) for(register int i=j;i<=n;i++)
#define lowbit(i) i&(-i)
using namespace std;
int k,n,c[20],p[100010],dp[1100000],sum[100010];
inline int read(){
    int datta=0;char chchc=getchar();bool okoko=0;
    while(chchc<'0'||chchc>'9'){if(chchc=='-')okoko=1;chchc=getchar();}
    while(chchc>='0'&&chchc<='9'){datta=datta*10+chchc-'0';chchc=getchar();}
    return okoko?-datta:datta;
}
int main(){
    k=read();n=read();
    F(i,1,k)
        c[i]=read(),c[0]+=c[i];
    F(i,1,n)
        p[i]=read(),sum[i]=sum[i-1]+p[i];
    mem(dp,-1);
    dp[0]=0;
    int ans=2147483647;
    F(sta,0,(1<<k)-1){
        if(dp[sta]==-1)
            continue;
        if(dp[sta]==n){
            int _ans=0;
            F(i,1,k)
                if(sta&(1<<(i-1)))
                    _ans+=c[i];
            ans=min(ans,_ans);
            continue;
        }
        F(i,1,k){
            if(sta&(1<<(i-1)))
                continue;
            int l=dp[sta]+1,r=n,mid,_ans=-1;
            while(l<=r){
                mid=(l+r)>>1;
                if(sum[mid]<=sum[dp[sta]]+c[i])
                    l=mid+1,_ans=mid;
                else
                    r=mid-1;
            }
            if(_ans==-1)
                continue;
            dp[sta|(1<<(i-1))]=max(dp[sta|(1<<(i-1))],_ans);
        }
    }
    printf("%d\n",ans==2147483647?-1:c[0]-ans);
    return 0;
}

转载于:https://www.cnblogs.com/hzf29721/p/10393345.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值