[2019 CSP-S赛前集训] [P1450] [蒟蒻Xx_queue学DP] 2.硬币购物
题目链接:https://www.luogu.org/problem/P1450
题目描述
硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买si的价值的东西。请问每次有多少种付款方法。
分析:相信各位小伙伴和我一样,第一眼看这个题:这不是个多重背包的板子题吗??
But,看一下数据范围,这么暴力的做法根本行不通啊,T飞去了;
那有什么巧妙的方法来解决这道题呢?
如果说对于每个询问,没有硬币数量的限制,那就是个完全背包是吧,相信大家一定都能看出来的;
那么我们是不是可以想一想,拿这个完全背包减掉不符合限制的那一部分,是不是就是答案了呢?
这个还是比较好办的啊,不合法的方案就是f[s−(dj+1)∗cj](j为第几种硬币),想一想为什么?
我先固定把这种硬币用上dj+1个,这样得到的结果一定是不合法的,那么不合法的方案数自然就是:总和扣掉dj+1个cj硬币后的完全背包方法种数;
是不是非常巧妙?(没错我也是看了题解的)
所以最终答案就是f[s]−∑4j=1f[s−(dj+1)∗cj];
所以要怎样求这个答案呢?这里还有一点点小问题:你使用硬币1的不合法方案与你使用硬币2的不合法方案有重叠,怎么解决?
容斥一下就好了;怎么容斥?详见代码.
注意事项:1.开long long!不开只有50分亲测;
2.预处理f[0]=1(凑齐0元只有一种方案:什么都不选),否则程序输出为0;
#include <bits/stdc++.h>
#define int long long
#define N (100000+5)
using namespace std;
int tot,ans,ss;
int c[5],d[5],dp[N];
void dfs(int k,int s,int p){//当前第k种硬币,剩余s元要凑,p为符号判断;
if(s<0) return;//凑够了(多了),return
if(k==5){//硬币种类已经选完
ans+=dp[s]*p;//ans加一下,容斥
return;
}
dfs(k+1,s,p);//符合限制的方案
dfs(k+1,s-(d[k]+1)*c[k],-p);//不符合限制的方案,注意变号
}
signed main(){
for(int i=1;i<=4;i++)scanf("%lld",&c[i]);
scanf("%lld",&tot);
dp[0]=1;
for(int i=1;i<=4;i++){
for(int j=c[i];j<=100000;j++){
dp[j]=dp[j]+dp[j-c[i]];
}
}//完全背包
for(int i=1;i<=tot;i++){
ans=0;
for(int j=1;j<=4;j++)scanf("%lld",&d[j]);
scanf("%lld",&ss);
dfs(1,ss,1);
printf("%lld\n",ans);
}
return 0;
}
但愿大家能看懂吧......
反正我们机房的某些大佬写的博客实在是晦涩难懂,满篇幅的都是"......即可","显然......";
实在是跟不上dalao们的思维啊,看来我还要加油啊!
转载请注明出处--Xx_queue
分类:
蒟蒻Xx_queue学DP系列
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从二进制到误差:逐行拆解C语言浮点运算中的4008175468544之谜
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· C# 13 中的新增功能实操
· Ollama本地部署大模型总结
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(4)
· 卧槽!C 语言宏定义原来可以玩出这些花样?高手必看!
· langchain0.3教程:从0到1打造一个智能聊天机器人