欲望以提升热忱,毅力以磨平高山!|

XichenOC

园龄:1个月粉丝:4关注:0

2025-02-07 15:50阅读: 5评论: 0推荐: 1

P3092 [USACO13NOV] No Change G

P3092 [USACO13NOV] No Change G

题目翻译:

给定 \(k\) 个硬币,有其对应的价值。并给定 \(n\) 个物品和其价格,求每次只能付一个硬币的前提下,按顺序买完物品最后能剩下的最多钱。

思路:

我们发现题目中硬币的数目很少,那我们可以用状态压缩来压缩已经使用过的硬币,那样我们可以用状压 \(dp\) 来维护当前状态下所能买的最多物品,这样就只需要在最多物品等于 \(n\) 时计算剩余钱数,在统计答案即可。

实现:

维护 \(dp[i]\) 表示 \(i\) 状态下能买到的最多物品数。只需要枚举状态 \(i\),在其中在枚举最后所使用的硬币,然后根据硬币价值二分查找到最后能买到的位置。若等于 \(n\) 就在枚举统计下所使用的硬币,和比较更新答案即可。

完整代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;;
int dp[N],sum[N];
int val[N];
int n;
int find(int id,int last){
    int l=last+1,r=n;
    while(l<=r){
        int mid=(l+r)>>1;
        if(sum[mid]-sum[last]==val[id]){
            return mid;
        }
        else if(sum[mid]-sum[last]<val[id]){
            l=mid+1;
        }
        else{
            r=mid-1;
        }
    }
    return r;
}
int main(){
    int k,ans=0x3f3f3f3f,tot=0;
    scanf("%d%d",&k,&n);
    for(int i=1;i<=k;i++){
        scanf("%d",&val[i]);
        tot+=val[i];
    }
    for(int i=1;i<=n;i++){
        int w;
        scanf("%d",&w);
        sum[i]=sum[i-1]+w;
    }
    for(int i=1;i<(1<<k);i++){
        for(int j=0;j<k;j++){
            if(i&(1<<j)){
                dp[i]=max(dp[i],find(j+1,dp[i^(1<<j)]));
                if(dp[i]==n){
                    int num=0;
                    for(int l=0;(1<<l)<=i;l++){
                        if(i&(1<<l)){
                            num+=val[l+1];
                        }
                    }
                    ans=min(ans,num);
                }
            }
        }
        
    }
    printf("%d",ans==0x3f3f3f3f?-1:tot-ans);
}

状压 \(dp\) 讲解

本文作者:XichenOC

本文链接:https://www.cnblogs.com/XichenOC/p/18702745

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   XichenOC  阅读(5)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起