【题解】P3092 [USACO13NOV]No Change G(状压 DP,双指针)
【题解】P3092 [USACO13NOV]No Change G
状压 DP+双指针好题。
题目链接
题意概述
有
现在要按顺序购买这
有
数据范围
思路分析
首先需要转化一步题意:将最多剩下多少面值的硬币转化为所有硬币面值之和-买所有物品的最小代价。
所有硬币面值之和这个就是
这个数据范围一看就是状压。
比较显然的是随着购买物品的增多,其面值之和单调递增。
所以我们可以考虑定义
那么最后答案就是
这里的
考虑如何求
假设当前状态是
可以枚举下一个选用的硬币是谁,假设选用的是
考虑如何转移。
首先,对于下一个选用的硬币
考虑如何求出这个最大的
我们可以预处理出一个数组
以样例为例,当
可以发现对于每一个
那么对于
那么我们的转移就完成了。
总时间复杂度
代码实现
//luoguP3092
//2022.9.22 22:02
#include<cstdio>
#include<iostream>
//#define debug1
//#define debug2
//#define debug3
//#define debug4
#define int long long
using namespace std;
const int maxk=20;
const int maxn=1e5+10;
int dp[1<<maxk],w[maxk],a[maxn];
int val[1<<maxk],f[maxk][maxn];
inline int read()
{
int x=0,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-48;ch=getchar();}
return x*f;
}
signed main()
{
int k=read(),n=read(),tot=0;
for(int i=1;i<=k;i++)w[i]=read(),tot+=w[i];
for(int i=1;i<=n;i++)a[i]=read();
for(int sta=0;sta<(1<<k);sta++)
{
for(int i=0;i<k;i++)
{
if(sta&(1<<i))val[sta]+=w[i+1];
}
}
int ans=1e18;
for(int sta=0;sta<(1<<k);sta++)
{
for(int i=0;i<k;i++)
{
if(sta&(1<<i))continue;
if(dp[sta]>=n)continue;
dp[sta|(1<<i)]=max(dp[sta|(1<<i)],f[i+1][dp[sta]+1]);
}
if(dp[sta]==n)ans=min(ans,val[sta]);
}
if(ans==1e18)cout<<-1<<'\n';
else cout<<tot-ans<<'\n';
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现