硬币购物

18 篇文章 0 订阅
7 篇文章 0 订阅

题目大意

传送门
在这里插入图片描述

思路

我觉得正常人的第一反应应该就是dp了吧,但是,实际上,它会超时,而且是开了 O ( 2 ) O(2) O(2)都A不了的那种超时,所以我们得换一个思路。但是,好像,如果没有限制的话,这不就是一个TMD 完全背包问题么?dp式连我这种蒟蒻都会秒列出 d p [ i ] = ∑ j = 1 4 d p [ i − c [ j ] ] dp[i]=\sum_{j=1}^{4}dp[i-c[j]] dp[i]=j=14dp[ic[j]],好,我们还是一个一个地搞吧,
如果半毛钱限制都没有,那么,答案就是 d p [ s ] dp[s] dp[s],如果只有一个限制,那么,答案就是 d p [ s ] − d p [ s − c [ 1 ] × ( d [ 1 ] + 1 ) ] dp[s]-dp[s-c[1]\times (d[1]+1)] dp[s]dp[sc[1]×(d[1]+1)],为哈子是这个玩意呢?这里的 s − c [ 1 ] × ( d [ 1 ] + 1 ) s-c[1]\times (d[1]+1) sc[1]×(d[1]+1)就是说对于第一种硬币,我取了超过限制个,然后意会一下估计就能意会到了。好,我们现在有一个的了,我们按照老土套路,我们可以看出这个就是容斥原理,那么,对于两种的情况,就该是 d p [ s ] − d p [ s − c [ 1 ] × ( d [ 1 ] + 1 ) − c [ 2 ] × ( d [ 2 ] + 1 ) ] dp[s]-dp[s-c[1]\times (d[1]+1)-c[2]\times (d[2]+1)] dp[s]dp[sc[1]×(d[1]+1)c[2]×(d[2]+1)],对于三个的情况,就应该是 d p [ s − ∑ i = 1 3 c [ i ] × ( d [ i ] + 1 ) ] dp[s-\sum_{i=1}^{3}c[i]\times (d[i]+1)] dp[si=13c[i]×(d[i]+1)],然后以此类推四个的情况,然后,一共有 2 4 2^4 24种情况(每个点越界或不越界),我们可以用优美的位运算来完成这个东西,就是一个容斥模板,代码附上,时间复杂度: O ( 4 × 100000 + 2 4 × t o t ) O(4\times 100000+2^4\times tot) O(4×100000+24×tot)

代码

#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm> 
using namespace std;

#define Int register int
#define int long long
#define MAXN 100005

int tot,c[5],dp[MAXN];

void init()
{
	dp[0] = 1;
	for (Int i = 1;i <= 4;++ i)
		for (Int j = c[i];j <= MAXN - 5;++ j)
			dp[j] += dp[j - c[i]];
}

void read (int &x)
{
	x = 0;char c = getchar();int f = 1;
	while(c < '0' || c > '9'){if(c == '-')f = -f;c = getchar();}
	while(c >= '0' && c <= '9'){x = (x << 3) + (x << 1) + c - '0';c = getchar();}
	x *= f;return ;
}

void write (int x)
{
	if (x < 0){x = -x;putchar('-');}
	if (x > 9) write (x / 10);
	putchar (x % 10 + '0');
}

signed main()
{
	for (Int i = 1;i <= 4;++ i) read (c[i]);
	read (tot);
	init();
	while (tot --)
	{
		int d[5] = {},s;
		for (Int i = 1;i <= 4;++ i) read (d[i]);
		read (s);
		int ans = 0;
		for (Int i = 0;i <= 15;++ i)
		{
			int cnt = 0;
			int tot = s;
			for (Int j = 1;j <= 4;++ j)
				if ((i >> j - 1) & 1)
				{
					tot -= c[j] * (d[j] + 1);
					cnt ^= 1;
				}
			if (tot < 0) continue;
			ans += (cnt == 0 ? 1 : -1) * dp[tot];
		}
		write (ans),putchar ('\n');
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值