ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

处理一类体积为实数的背包问题的技巧

2021-11-15 18:33:52  阅读:230  来源: 互联网

标签:背包 实数 sum int ai cost 体积 1000


CF366C Dima and Salad (背包 一个 trick)

CF366C Dima and Salad

题目要求取满足 ∑ a j ∑ b j = k \dfrac{\sum a_j}{\sum b_j}=k ∑bj​∑aj​​=k 时, ∑ a j \sum a_j ∑aj​ 的最大值,讨论如何表达背包体积,有如下技巧:

将原式移项,变式得到 ∑ ( a j − k b j ) = 0 \sum(a_j-kb_j)=0 ∑(aj​−kbj​)=0 于是发现,我们可以将 a j − k b j a_j-kb_j aj​−kbj​ 看作背包代价或体积,最后答案是 f ( 0 ) f(0) f(0) ,对于存在负数的问题,增添一个偏移量 Δ \Delta Δ 即可。

for(int i = 1;i <= n;i ++)
{
	cost[i] = a[i] - k*b[i];
	if(cost[i] > 0) maxn += cost[i];
	else minn += cost[i];
}
minn += d;maxn += d;
for(int i = 1;i <= n;i ++)
{
    if(cost[i] > 0)//注意此处体积/代价的符号对 01 背包顺序的影响
	{
		for(int j = maxn;j >= minn;j --)
			if(f[j] != -1)
				f[j + cost[i]] = max(f[j + cost[i]],f[j] + a[i]);
	}
	else
	{
		for(int j = minn;j <= maxn;j ++)
			if(f[j] != -1)
				f[j + cost[i]] = max(f[j + cost[i]],f[j] + a[i]);
	}
	f[cost[i] + d] = max(f[cost[i] + d],a[i]);
}
printf("%d",f[d]);

CF788C The Great Mixing ( 背包 同样的 trick )

CF788C The Great Mixing

观察到求满足 ∑ m a i ∑ m 1000 = n 1000 \dfrac{\sum^m a_i}{\sum^m 1000}=\dfrac{n}{1000} ∑m1000∑mai​​=1000n​ 的情况下,令 m m m 最小,显然,运用上面的 trick 我们又可以将其转化为将 a i − n a_i-n ai​−n 视为代价的背包问题,为了减少枚举量可以用队列储存可以被更新的点。对于枚举的边界问题,观察到题目的数据范围 a i ⩽ 1000 , n ⩽ 1000 a_i\leqslant 1000,n\leqslant 1000 ai​⩽1000,n⩽1000 ,由于我们的最终目标是将 ∑ ( a i − n ) = 0 \sum(a_i-n) = 0 ∑(ai​−n)=0 ,那么当当前的 ∑ ( a i − n ) \sum (a_i-n) ∑(ai​−n) 取到 − 1000 -1000 −1000 的时候,再取更小就没有意义了, a i a_i ai​ 最大就 1000 1000 1000 ,明明可能可以在下一步一次将代价总和加到 0 ,再取更小然后才加回来一定不是最优方案,于是可以知道,枚举范围为 [ − 1000 , 1000 ] [-1000,1000] [−1000,1000] 即可,为了保险,可以适当拓宽范围,只要不要令区间范围太大,比如 1 0 6 10^6 106 ,控制好区间范围即可。

int main()
{
	scanf("%d%d",&n,&k);
	for(int i = 0;i <= 5000;i ++) f[i] = inf;
	for(int i = 1;i <= k;i ++)
	{
		scanf("%d",&x);
		if(!vis[x - n + d])
		{
			f[x - n + d] = 1;vis[x - n + d] = 1;
			tot ++;a[tot] = x - n;
			q.push(x - n + d);
		}
	}
	for(int i = 0;i <= 5000;i ++) vis[i] = 0;
	for(;!q.empty();)
	{
		u = q.front();
		q.pop();
		for(int i = 1;i <= tot;i ++)
			if(a[i] + u >= 5 && a[i] + u <= 5000)
			{
				f[a[i] + u] = min(f[a[i] + u],f[u] + 1);
				if(!vis[a[i] + u])
				{
					vis[a[i] + u] = 1;
					q.push(a[i] + u);
				}
			}
	}
	if(f[d] == inf) printf("-1");
	else printf("%d",f[d]);
	return 0;
}

ARC060C Tak and Cards

AtCoder Regular Contest 060 C - Tak and Cards
求选择一些卡片使得的它们上面的整数的平均数恰好为 A A A 的方案数。
问题显然转化为求 ∑ m a i ∑ m 1 = A \dfrac{\sum^m a_i}{\sum^m 1}=A ∑m1∑mai​​=A 的方案数,同样运用 trick ,变式为求 ∑ ( a i − A ) = 0 \sum(a_i - A) = 0 ∑(ai​−A)=0 的方案数,于是背包即可。

#include<iostream>
#include<cstdio>

using namespace std;
typedef long long ll;
const int d = 3000;
int N,A,a[105];
ll f[6005];

int main()
{
	scanf("%d%d",&N,&A);
	for(int i = 1;i <= N;i ++)
	{
		scanf("%d",&a[i]);
	}
	f[a[1] - A + d] = 1;
	for(int i = 2;i <= N;i ++)
	{
		if(a[i] - A > 0)
		{
			for(int j = 6000;j >= 0;j --)
				if(f[j] > 0)
					f[j + a[i] - A] += f[j];
		}
		else
		{
			for(int j = 0;j <= 6000;j ++)
				if(f[j] > 0)
					f[j + a[i] - A] += f[j];
		}
		f[a[i] - A + d] ++;
	}
	printf("%lld",f[d]);
	return 0;
}

标签:背包,实数,sum,int,ai,cost,体积,1000
来源: https://blog.csdn.net/bell041030/article/details/121340266

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有