有一些东西必不可少(前后背包+二分/前缀和优化)

这篇具有很好参考价值的文章主要介绍了有一些东西必不可少(前后背包+二分/前缀和优化)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

题意:
给定n个物品,每个物品具有权值a[i],在这些物品里选出若干个物品,使得权值和>=k,就说是一个合法的方案。对于一个物品,如果存在一个合法的方案,在这个方案中去掉它以后方案就变成不合法了,就说这个物品是“必要的”。求有多少个物品是必要的。
n<=5000,k<=5000
思路: 如果a[i]>=k,一定必要,因为存在只包含他自己的方案,去掉他就不合法了。对于a[i]<k,如果其他物品能够凑出一个方案,权值和在[k-a[i],k-1]之间,该物品同样是必要的。所以想到一种朴素的想法,就是去掉某一个物品,然后依次进行01背包。但是这样很lao,因为时间复杂度会达到n * n * k. 可以用可逆背包链接
或者用一种预处理前缀后缀背包的手法,比如说dp[i][j]表示前i个物品,能否凑出j。dp2[i][j]表示从n到i这些物品,能否凑出j.
预处理dp之后,对于每个物品i,看是否存在dp[i-1][l]和dp[i+1][r],他们都是合法方案,且满足 k-a[i]<=l+r<=k-1.
显然枚举l、r很慢,但是可以只枚举l,另一个通过二分得到。

枚举l,此时r满足k-a[i]-l<=r,lower_bound得到r的左边界,之后判断r是否满足r<=k-1-l,即可判断是否存在合法方案。
但是这样带一个log,像python这种运行慢的语言可能会被卡掉。

所以想到了用前缀和优化,我们还是枚举l,但是r不用二分了。sum[i][j]存dp2某一行的前缀和,表示用n到i这些数,和<=j的方案数。
有一些东西必不可少(前后背包+二分/前缀和优化),c++,算法
根据左侧数j的大小,分情况讨论右侧x的范围。可以发现这两种情况的方案数分别对应了sum[i+1][k-1-j]和sum[i+1][k-1-j] - sum[i+1][k-a[i]-j-1],这就是前缀和的魅力。如果不是很理解,建议仔细思考一下sum数组是这些数能凑出<=j的方案数,用能凑出<=10的方案数减去<=5的方案数,就是凑出的值在[6,10]之间的方案数了。
时间复杂度: O(nk*log(k))或O(nk)
代码:
二分版:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
const int N = 5002;
int n,m,k,T;
bool dp[N][N];
bool dp2[N][N];
int b[N];
int a[N];
int main() {
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    cin>>m>>k;
    n = 0;
    int ans = 0;
    for(int i=1;i<=m;++i)
    {
    	cin>>b[i];
    	if(b[i]>=k) ans++;
    	else a[++n] = b[i];
	}
	dp[0][0] = 1;
	dp2[n+1][0] = 1;
	for(int i=1;i<=n;++i)
	{
		for(int j=0;j<=k;++j)
		{
			dp[i][j] |= dp[i-1][j]; 
			if(j>=a[i]) dp[i][j] |= dp[i-1][j-a[i]];
		}
	}
	for(int i=n;i>=1;--i)
	{
		for(int j=0;j<=k;++j)
		{
			dp2[i][j] |= dp2[i+1][j];
			if(j>=a[i]) dp2[i][j] |= dp2[i+1][j-a[i]];
		}
	} 
//	cout<<dp2[5][2]<<"!\n";
	for(int i=1;i<=n;++i)
	{
//		cout<<i<<":"<<a[i]<<"?\n";
		vector<int> l,r; 
		for(int j=0;j<=k;++j)
		{
			if(dp[i-1][j]) l.push_back(j);
			if(dp2[i+1][j])
			{
				r.push_back(j);
			}
		}
		bool flag = 0;
		int idx1 = lower_bound(l.begin(),l.end(),k-a[i]) - l.begin();
		int idx2 = lower_bound(r.begin(),r.end(),k-a[i]) - r.begin();
		if(idx1!=l.size() && l[idx1] <= k-1) 
		{
			flag = 1;
//			cout<<i<<":"<<"?\n";
		}
		if(idx2!=r.size() && r[idx2] <= k-1) 
		{
			flag = 1;
//			cout<<i<<" "<<idx2<<":"<<r[idx2]<<"?\n";
//			cout<<i<<":"<<"??\n";
//			for(auto item:r) cout<<item<<"???\n";
		}
//		cout<<i<<":"<<flag<<"?\n";
		for(int j=l.size()-1;!flag && j>=0;--j)
		{
			int now = l[j];
			int idx = lower_bound(r.begin(),r.end(),k-a[i]-l[j]) - r.begin();
			if(idx!=r.size() && now + r[idx] <= k-1) flag = 1;
		}
		if(flag) ans ++ ;
	}
	cout<<ans;
    return 0;
}
/*
6 20
10 4 3 10 25 2

10 4 3 10 2
*/

前缀和版:文章来源地址https://www.toymoban.com/news/detail-714804.html

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N = 5002;
int n,m,k,T;
bool dp[N][N];
bool dp2[N][N];
int b[N];
int a[N];
int sum[N][N]; //从n到i,<=j的方案数,方便统计方案 
int main(void)
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>m>>k;
    n = 0;
    int ans = 0;
    for(int i=1;i<=m;++i)
    {
    	cin>>b[i];
    	if(b[i]>=k) ans++;
    	else a[++n] = b[i];
	}
	//从前向后dp、从后向前dp,求前i个数能否凑出j、从n到i能否凑出j 
	dp[0][0] = 1;
	dp2[n+1][0] = 1;
	for(int i=1;i<=n;++i)
	{
		for(int j=0;j<=k;++j)
		{
			dp[i][j] |= dp[i-1][j]; 
			if(j>=a[i]) dp[i][j] |= dp[i-1][j-a[i]];
		}
	}
	for(int i=n;i>=1;--i)
	{
		for(int j=0;j<=k;++j)
		{
			dp2[i][j] |= dp2[i+1][j];
			if(j>=a[i]) dp2[i][j] |= dp2[i+1][j-a[i]];
			if(j==0) sum[i][j] = 1;
			else sum[i][j] = sum[i][j-1] + dp2[i][j];
		}
	} 
	//问题转化为对于每个a[i],如果a[i]>=k,那么肯定符合,因为存在一种只有它自己的方案,它不可或缺
	//;否则的话,如果存在某种方案坐落在[k-a[i],k-1]之间,a[i]同样符合 
	for(int i=1;i<=n;++i) //枚举当前的数 
	{
		
		for(int j=0;j<k;++j) //枚举前i-1个数的和
		{
			if(!dp[i][j]) continue;
			int l = k-a[i]-j;
			int r = k-1-j;
			
			int cnt = 0;//右边需要凑的方案数量 
			if(j>=k-a[i]) //l<=0
			{
				//j+x<=k-1, x<=k-1-j
				cnt = sum[i+1][r]; //右边,<=k-1-j的方案数 
			}
			else //l>0
			{
				//j+x>=k-a[i],x>=k-a[i]-j
				//j+x<=k-1,x<=k-1-j
				//求k-a[i]-j<=x<=k-1-j的方案数
				cnt = sum[i+1][r] - sum[i+1][l-1]; 
			}
			if(cnt>0)
			{
				ans ++ ;
				break;
			}
		} 
	}
	cout<<ans;
	return 0;
}
/*
6 20
10 4 3 10 25 2
*/

到了这里,关于有一些东西必不可少(前后背包+二分/前缀和优化)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【AI绘画】Stablediffusion必不可少的使用方法之关键词(2)

            我相信很多已经下载好Stablediffusion或者midjourney软件的朋友,第一反应都是看着满屏看不懂的各种选项发懵吧,而当你稳住心神,准备在文生图或者图生图这两块基础操作力大显身手,想创造出属于自己的艺术设计之时,难免会对着下面这个框框陷入两难:我应该填什

    2024年02月02日
    浏览(52)
  • Qt | QWidget 自定义消息处理函数(捕获调试信息),调试和测试必不可少

    # 01 函数qInstallMessageHandler     qInstallMessageHandler 是 Qt 中用于安装自定义消息处理函数的函数。在 Qt 应用程序中,可以使用 qInstallMessageHandler 来接管 Qt 的消息输出,以便自定义消息的处理和记录。 #02  myMessageOutput(QtMsgType type, const QMessageLogContext context, const QString msg)    

    2024年03月22日
    浏览(51)
  • 【npm link】Node命令中的npm link命令的使用,还有CLI全局命令的使用,开发命令行工具必不可少的部分

    😁 作者简介:一名大四的学生,致力学习前端开发技术 ⭐️个人主页:夜宵饽饽的主页 ❔ 系列专栏:NodeJs 👐学习格言:成功不是终点,失败也并非末日,最重要的是继续前进的勇气 ​🔥​前言: 本文是关于Node命令中的npm link命令的详细使用,还有脚手架的背后原理,如

    2024年01月16日
    浏览(46)
  • BLE调制与解调的一些东西

    BLE是GFSK的IQ调制 所谓IQ调制,就是利用IQ两个分量序列去控制两路正交信号,I和Q两个序列可以是任意数字,也可以是符合某些规律的序列。 总的原理公式就是: 看这篇博客的图片,很清楚。 https://blog.csdn.net/qq_41019681/article/details/111305603 想要更深入的理解,就看这篇,别人写

    2024年02月02日
    浏览(39)
  • php使用chatGPT生成一些东西做一个记录

    好久没写了,这么长时间都去坐一些自己感兴趣的事情去了。 之前使用chatgpt-3,效果一直不咋好,这里我们来说说各个版本区别 gpt-3收费成本可以接受,生成的内容对话有点不太聪明的样子 git-3.5-turbo收费相对来说低,生成文本质量还是蛮高的,虽然有可能存在一点废话,但是

    2024年02月15日
    浏览(47)
  • 码一些有用的东西网站的域名被拦截怎么办? 教你快速解除各种拦截

    今天跟大家讲解一下网站域名被拦截怎么办?怎么去解决,相信这个问题一直都是很多人的困惑吧,其实大部分行业的拦截都是可以进行处理的,针对新人来讲可能还不知道什么网站域名被拦截,下面我详细来讲解下。 什么是网站域名拦截? 网站拦截就是别人投诉了你的网

    2023年04月19日
    浏览(51)
  • 个人对前后端分离的一些看法

    内容简介:前端开发过程中能完全不依赖后端的才是真正的前后端分离指的是工作过程中,前端的的代码中往往会掺杂一些后端的逻辑。后端返回了一个json对象 前端开发过程中能完全不依赖后端的才是真正的前后端分离 指的是工作过程中,前端的的代码中往往会掺杂一些后

    2024年02月13日
    浏览(40)
  • 贝塞尔曲线 PH曲线 C曲线 B样条 NURBS样条曲线 三次Cardinal样条曲线对比 也涉及到不同曲线加速度的一些东西,不过有待细化

    本文很多直接截图论文的,因为不需要重复造轮子,对比也只是为了选择更佳的路径规划曲线,对比于B曲线,时间不够,概括会有所疏漏,下表是曲线的对比表格,看完可以直接看下面,也涉及到不同曲线加速度的一些东西,不过有待细化,2022/3/17后来上了高等工程数学,如

    2024年02月03日
    浏览(41)
  • mac东西拷不进硬盘怎么回事 mac东西拷不进硬盘怎么办 mac硬盘读不出来怎么解决 mac拷贝不了东西到u盘

    有时候我们在使用mac的过程中,可能会遇到一些问题,比如mac东西拷不进硬盘。这是一种很常见的情况,但是会影响我们的工作和生活。那么,mac东西拷不进硬盘是怎么回事呢?mac东西拷不进硬盘又该怎么办呢?本文将为你解答这两个问题。 mac东西拷不进硬盘的原因可能有以

    2024年02月19日
    浏览(61)
  • @Value是个什么东西

    对注解不了解的可以看一下: Java注解,看完就会用 首先我们要明确: @Value 是 Spring 框架的注解。 它有什么作用呢? @Value 通过注解将 常量 、 配置文件中的值 、其他 bean的属性值 注入到变量中,作为变量的初始值。 顾名思义,就是把一个写死的值赋给对应变量,形式如下

    2024年02月03日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包