首页 > 其他分享 >一种优化 01 可行背包的方法

一种优化 01 可行背包的方法

时间:2024-07-30 20:51:43浏览次数:8  
标签:vector cj ci 可行 int res 01 背包 dp

source:abc221g

有 \(n\) 个物品,体积分别为 \(a_{1,2,\dots,n}\) ,要求从中选出若干个物品使得体积和为 \(V\) 。令 \(A=\max a_i\) ,\(V\le nA\) 。一般的 01 背包做法是 \(O(n^2A)\) 的,但存在一种相对简单的做法可以做到复杂度 \(O(nA)\) 。下面描述这个做法。

首先任意排列这个物品序列 \(a\) ,然后暴力从前往后取,直到当前取到的体积和 \(\ge V\) (如果取不到则显然无解)。

然后这些物品被分为两类,第一类是被选中的,第二类是没有被选中的,如果存在一个合法方案,那么肯定是从第一类中去掉若干个物品,再加入第二类中的若干个物品。

为了减小值域,有一个非常直觉的做法:如果当前的体积和 \(\gt V\) 就考虑一个删除操作,否则就考虑加入操作。这个做法显然是对的,并且把 dp 的值域从 \(O(nA)\) 降到了 \(O(A)\) (注意实际实现与描述略有不同)。考虑 dp ,可以令 \(f_{l,r,w}\) 表示:已经确定区间 \([l,r]\) 的方案,区间之外左边全都被选择,右边全都不被选择,是否存在一种方案满足体积和为 \(w\) 。转移类似区间 dp,从 \(f_{t+1,t}\) 开始逐步向左向右扩展,其中 \(t\) 是一开始选的物品数量。不过这样 dp 的复杂度还是 \(O(n^2A)\) 的。

注意到 dp 数组的内容相对简单(只有 01),因此考虑变更一下 dp 方式,令 \(g_{r,w}​\) 表示最大的一个 \(l​\) 满足 \(f_{l,r,w}=1​\) ,同样是可以转移的,分为三类:

  • \(g_{r,w}\leftarrow g_{r-1,w}\) ,对应最右边的元素不选的情况

  • \(g_{r,w+a_r}\leftarrow g_{r-1,w}\) ,对应最右边的元素要选的情况

  • 对于所有 \(l\) 满足 \(l \lt g_{r,w}\) :\(g_{r,w-a_l}\leftarrow l\) ,也就是向左扩展

这三类转移做的都是取 \(\max\) 操作。

前两个转移的复杂度是 \(O(nA)\) 的。考虑第三个转移,由于需要枚举 \(l\) 所以复杂度仍然是 \(O(n^2A)\) 。事实上还可以做一步优化,发现所有 \(l\lt g_{r-1,w}\) 的 \(l\) 都是没有用的,因为完全可以先从 \(f_{*,r-1,w}\) 转移到 \(f_{l,r-1,w-a_l}\) 然后再向右扩展,所以实际上只需要枚举 \(l\in[g_{r-1,w}, g_{r,w})\) 。这样的话对于某个特定的 \(w\) 容易得出枚举 \(l\) 的总数是 \(O(n)\) 的,因此总复杂度被优化到 \(O(nA)\) 。

注意转移顺序。

放一个代码,这份代码可以输出方案。

#include<bits/stdc++.h>
using namespace std;

bool dp(int n, int A, int V, vector<int> a, vector<bool> &res){
	if(V==0){
		res.resize(n);
		fill(res.begin(), res.end(), 0);
		return true;
	}
	int sum=0, t=0;
	while(t<n && sum<V){
		sum+= a[t];
		t++;
	}
	if(sum<V){
		return false;
	}
	int D=A-V;
	vector<vector<int>> f(n, vector<int>(A*2+1, -1)), lst(n, vector<int>(A*2+1));
	
	auto upd=[&](int pi,int pj,int val,int info){
		if(val>f[pi][pj]){
			f[pi][pj]=val;
			lst[pi][pj]=info;
		}
	};
	
	f[t-1][sum+D]=t;
	for(int r=t-1; r<n; r++){
		if(r>=t){
			for(int w=0; w<=A*2; w++){
				upd(r, w, f[r-1][w], 0);
			}
			for(int w=0; w<=A*2-a[r]; w++){
				upd(r, w+a[r], f[r-1][w], 1);
			}
		}
		for(int w=A*2; w>=0; w--){
			for(int l=(r==0 || f[r-1][w]==-1? 0: f[r-1][w]); l<f[r][w]; l++){
				if(w>=a[l]){
					upd(r, w-a[l], l, 2);
				}
			}
		}
	}
	if(f[n-1][V+D]==-1){
		return false;
	}
	res.resize(n);
	fill(res.begin(), res.begin()+t, 1);
	fill(res.begin()+t, res.end(), 0);
	int ci=n-1, cj=V+D;
	while(!(ci==t-1 && cj==sum+D)){
		int op=lst[ci][cj];
		if(op==0){
			ci--;
		}
		else if(op==1){
			res[ci]=1;
			cj-= a[ci];
			ci--;
		}
		else{
			res[ f[ci][cj] ]=0;
			cj+= a[ f[ci][cj] ];
		}
	}
	return true;
}

signed main(){
  	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
  	
  	int n,V; cin>>n>>V;
  	vector<int> a(n);
  	for(auto &x:a){
  		cin>>x;
  	}
  	vector<bool> res;
  	bool ans=dp(n, *max_element(a.begin(), a.end()), V, a, res);
  	cout<< ans <<'\n';
  	if(ans){
		for(auto x:res){
			cout<<x<<' ';
		}
		cout<<'\n';
  	}
}

标签:vector,cj,ci,可行,int,res,01,背包,dp
From: https://www.cnblogs.com/fanjambot3663/p/18333331

相关文章

  • (10-2-01)智能行为决策算法:常用的智能行为决策算法-------马尔可夫决策过程(MDP)
    10.2 常用的智能行为决策算法在实际应用中,智能行为决策算法在自动驾驶系统中各有其独特的优势和应用场景,通过合理组合和优化,能够有效提升自动驾驶的安全性、可靠性和效率。在本节的内容中,将详细讲解常用的智能行为决策算法的用法。10.2.1 马尔可夫决策过程(MDP)马尔可夫......
  • 2024“钉耙编程”中国大学生算法设计超级联赛(1)1012并
    题目大意:给出n个矩形,求被k个矩形覆盖的面积的并集的期望,输出k为1-n的所一答案思路:由于是求期望所以是求出所有情况的和再除以可能的情况,每一种情况中的面积都由--同时被1个矩形覆盖,同时被两个矩形覆盖······同时被k个矩形覆盖组成,而且不难得出当k一定时,取被m个矩形覆盖的......
  • [极客大挑战 2019]PHP
    [极客大挑战2019]PHP首先,进入网址,发现有一个非常有趣的动态画面,你可以用毛线球逗小猫玩,小猫也会与你互动,真不错!咳咳,返回正题,上面提示一段文字,说他有网站备份的习惯,查看网页源码什么信息都没有,应该是把关键文件放到别的路径了。去尝试一下看看,这里有一些常用的网站备份路径:常用......
  • [EIS 2019]EzPOP 代码审计 死亡绕过
    点击查看代码<?phperror_reporting(0);classA{protected$store;protected$key;protected$expire;publicfunction__construct($store,$key='flysystem',$expire=null){$this->key=$key;$this->sto......
  • Solution - Atcoder APC001E Antennas on Tree
    首先考虑判定什么样的选取是合法的。考虑到令任意一个点\(u\)为根。若\(u\)有至少两个子树没有点选中,那么这两个子树是无法区分的。所以可以知道需要满足任意一个点为根,都至多存在一个子树内部没有选中的点。接下来就要贪心的选出最少的点了。考虑对于每个点的限制都是子......
  • nohup ./minio server --address :9000 --console-address :9001
    [root@ecm-8cc1minio]#./minioserver/opt/minioINFO:Formatting1stpool,1set(s),1drivesperset.INFO:WARNING:Hostlocalhasmorethan0drivesofset.Ahostfailurewillresultindatabecomingunavailable.MinIOObjectStorageServerCopyright:20......
  • [RoarCTF 2019]Easy Java
    [RoarCTF2019]EasyJavaStep1点击help按钮后发现:URL变成:url/Download?filename=help.docx而回显:java.io.FileNotFoundException:{help.docx}而当我尝试尝试POST,发现文件成功下载:Step2发现可能的漏洞点后,结合WEB-INF相关知识(见文末)可以下载WEB-INF/web.xmlPOST参数......
  • [rCore学习笔记 019]在main中测试本章实现
    写在前面本随笔是非常菜的菜鸡写的。如有问题请及时提出。可以联系:[email protected]:https://github.com/WindDevil(目前啥也没有批处理操作系统的启动和运行流程要想把本章实现的那些模块全部都串联在一起以实现运行一个批处理操作系统,回顾本章内容,思考批处理操作......
  • C++ - VS2019配置pthread线程库
    1.说明在VS里用MS编译器不能直接调用pthread库,需要先自行下载该库:http://sourceware.org/pub/pthreads-win32/pthreads-w32-2-9-1-release.zip解压后用得到的只有Pre-built.2文件夹下的文件。2.配置如下图分别配置三大项:包含目录-->...pthreads-w32-2-9-1-release\Pre-bu......
  • 洛谷题单指南-前缀和差分与离散化-P3017 [USACO11MAR] Brownie Slicing G
    原题链接:https://www.luogu.com.cn/problem/P3017题意解读:将一个r*c的矩阵,横向切成a条,每一条纵向切除b块,计算每一块子矩阵之和的最小值最大是多少。解题思路:要计算最小值中最大的,直觉上可以采用二分,下面来分析单调性:给定一个子矩阵块之和的值,值越小可以划分的条数、块数就越多......