洛谷P1450 [HAOI2008]硬币购物(背包问题,容斥原理)
2024-08-26 18:43:20
洛谷题目传送门
我实在是太弱了,第一次正儿八经写背包DP,第一次领会如此巧妙的容斥原理的应用。。。。。。
对每次询问都做一遍多重背包,显然T飞,就不考虑了
关键就在于每次询问如何利用重复的信息
我这么弱,当然是想不到容斥原理的啦
暂且先当成完全背包,每种硬币可使用无限次,预处理\(f\)数组,\(f[i]\)等于买价值\(i\)的东西的总方案数
然后就要从中减去不合法的。首先肯定会有一种硬币超额使用,第\(j\)中硬币等于说强制选了\(d_j+1\)个,剩下的依然随便选,那么第
\(j\)种硬币超额的不合法的方案数等于\(f[s-(d_j+1)*c_j]\),于是从答案里减去\(\sum_{j=1}^4f[s-(d_j+1)*c_j]\)
还要注意,第一种第二种都超额、第一种第三种都超额、第一种第四种都超额、第二种第三种都超额、第二种第四种都超额、第三种第四种都超额的方案在上一步中都被减了两次,所以额外都加一次回来。。。。。。(接着把容斥做下去就不说了)
复杂度降到\(O(4maxs+4×2^4tot)\),轻松通过
注意开longlong就好啦
#include<cstdio>
#define R register
typedef long long LL;
const int S=100009;
LL f[S]={1ll};
int main(){
R int c[4],d[4],tot,i,j,k,now,s,ss,tmp;
R LL ans;
for(j=0;j<4;++j)scanf("%d",&c[j]);
scanf("%d",&tot);
for(j=0;j<4;++j)
for(i=c[j];i<S;++i)
f[i]+=f[i-c[j]];//完全背包预处理
while(tot--){
for(j=0;j<4;++j)scanf("%d",&d[j]);
scanf("%d",&s);
ans=f[s];
for(ss=1;ss<=15;++ss){//二进制数枚举集合,容斥
now=s;
for(tmp=ss,j=k=0;tmp;tmp>>=1,++j)
if(tmp&1)k^=1,now-=(d[j]+1)*c[j];
//注意k的作用,判断奇偶
if(now>=0)k?ans-=f[now]:ans+=f[now];
}
printf("%lld\n",ans);
}
return 0;
}
最新文章
- CSS系列:CSS中盒子模型
- Unity3D的四种坐标系
- SQL 递归树 子父节点相互查询
- C++二维码相关库编译
- chroot详解
- [Java] 过滤文件夹
- GotFocus和PreviewLeftButtonDown事件
- B - Numbers That Count
- Ubuntu14.04安装Mongodb
- 关于C#文件复制(递归)
- 关于金额,重量等浮点数的数据库字段设计(用Int,Long代替浮点数计算)
- hadoop 基本命令
- Ansible(一) 配置安装
- FineUIPro/Mvc/Core v5.4.0即将发布(Core基础版,新功能列表)!
- Nowcoder contest 370B Rinne Loves Graph 【分层图最短路】
- java枚举类型
- 顺序队列(C语言)
- MDX 脚本语句 -- Scope
- 获取天气预报API5_统计最容易生病时间段
- gtk_init()