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 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步