row_number 和 cte 使用实例:背包问题

news2024/11/16 6:00:33

row_number 和 cte 使用实例:背包问题

  • 背包问题
    • 01背包
      • 解决同一行数据需要引用两次的问题
      • 对 for xml 的结果进行引用时的处理
    • 完全背包
    • 多重背包
  • 小结

背包问题

最近老顾从新把算法捡了起来,碰到了各种各样以前没见过的,工作中没遇到的问题,很多问题老顾都是一头雾水,完全没有思路,比如。。。背包问题?

背包问题(Knapsack problem)是一种组合优化的NP完全问题。问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。问题的名称来源于如何选择最合适的物品放置于给定背包中。相似问题经常出现在商业、组合数学,计算复杂性理论、密码学和应用数学等领域中。也可以将背包问题描述为决定性问题,即在总重量不超过W的前提下,总价值是否能达到V?它是在1978年由Merkle和Hellman提出的。

好家伙。。。。这问题够古老的,老顾78年才生人啊。

在看到各种题解之前,说实话,老顾完全不会解这个,太苦恼了,只能学别人的题解,而且因为学历问题,公式也看不懂,仅仅做到了会用这个解法罢了。

关于背包问题的描述,大家可以自行到百度百科了解,总之呢,算法很简单,实现很容易,开发语言表示这都不是事。

01背包

在使用了各种编程语言用各种算法实现了背包的解法后,老顾突然想到,数据库是否可以实现?不用流程控制?嘿嘿,Sqlserver 表示不服!

在各种背包解法里,老顾选择了二维数组方式,因为物品,重量,价值,这些加起来就是一个二维关系表了,所以,用这个方式不需要大动干戈了。

假设有5个物品 A、B、C、D、E,分别重10、12、11、16、10,分别价值12、16、61、24、15,问,在总计40承重的空间内,最大价值是多少?

这是一个最简单的 01 背包问题,每个物品只能用一次。很好,我们先用 cte 把原始数据弄出来。

;with t as (
	select 'A' name,10 w,12 p
	union all select 'B',12,16
	union all select 'C',11,16
	union all select 'D',16,24
	union all select 'E',10,15
)
select * from t

在这里插入图片描述
然后,老顾的思路非常简单啊,就是用 cte 递归,来实现背包计算,不过,看过老顾上一篇文章《row_number 和 cte 使用实例:考场监考安排》都知道,cte 的限制很多很多,不能多次递归,不能用外关联查询,不能列转行行转列等等等等。所以,老顾的变通方法,就是用 for xml 进行递归之间的数据传递。

那么,这次实现 01 背包问题也同样是如此。为了避免物品名有重复的问题,我们还是用 row_number 来排下序。

with ...
,t1 as (
	select *,row_number() over(order by name) rid from t
)
select * from t1

在这里插入图片描述
然后,还是用 master…spt_values 做数据填充,先实现第一个物品的背包数据。

with ...
select * from t1
cross apply (
	select number,(case when number<w then 0 else p end) cp
	from master..spt_values
	where type='p'
	and number<=40
) b
where rid=1

在这里插入图片描述
number 就表示称重的数量,我们可以看到当 number 与 w 相同的时候,背包内的价值数量就发生变动了。那么,如果我们用4个left join ,其实就可以完成背包的后续计算了,不过需要记得当新物品加入时,需要计算多物品价值,和原价值中,取最大的罢了。

不过,因为是要解决 01 背包问题,咱们最后的目的是具有泛用性,所以不确定到底有多少物品,所以用 join 方式解决就变得不可行了。还是要回到 cte 递归。而为了传递数据,我们需要将价值数据,作为参数传递给下一次使用,所以,还是得用 for xml。

with ...
select * 
from t1 
cross apply (
	select stuff((
		select ',' + convert(varchar,(case when number < w then 0 else p end))
		from master..spt_values 
		where type ='p' and number<=@place
		order by number
		for xml path('')
	),1,1,'') cols
) b
where rid=1

在这里插入图片描述

解决同一行数据需要引用两次的问题

在背包问题中,二维数组解法的最关键的一个步骤,就是需要比较当物品加入后的价值和原价值进行对比,不明白的小伙伴,自行搜索 01 背包问题,很多大佬都有填表格的解法说明。

而 cte 递归中,无法多次引用递归内容,所以,我们这里用 for xml 解决了,对 for xml 的数据进行拆分,从新变成多行数据。而这个数据引用,是可以无限次使用的哦。

比如,现在我们的 cols 列的数据是:

0,0,0,0,0,0,0,0,0,0,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12

表示40个格子的价值,这是一个字符串数据了,需要拆分成40行的数值类型数据。这里有几个办法:

1、高版本的SqlServer自带 string_split,需要 SqlServer 2016 以上,在切割后用 row_number 追加一下行号。
2、自行制作一个字符串切割函数,对版本没有要求,最好能自带行号,如果没有,也用 row_number 追加行号
3、使用正则,需要 SqlServer 2005 以上,开 clr

这里一定要追加行号哦,这个行号就是对应的格子承重哦,需要注意,用 row_number 追加行号的,需要减去1,否则承重计算会有误差的哦。

那么最后的递归部分完成后,查询指令就如下了。


declare @place int
set @place = 40

;with t as (
	select 'A' name,10 w,12 p
	union all select 'B',12,16
	union all select 'C',11,16
	union all select 'D',16,24
	union all select 'E',10,15
),tf as (
	select a.* 
	from t a
	cross apply (
		select number
		from master..spt_values
		where number<=(@place/w) and type='p'
	) b
),t1 as (
	select *,row_number() over(order by name) rid from t
),t2 as (
	select * 
	from t1 
	cross apply (
		select stuff((
			select ',' + convert(varchar,(case when number < w then 0 else p end))
			from master..spt_values 
			where type ='p' and number<=@place
			order by number
			for xml path('')
		),1,1,'') cols
	) b
	where rid=1
	union all
	select name,w,p,rid,ncols 
	from (
		select a.*,cols
		from t1 a,t2 b 
		where b.rid+1=a.rid
	) a
	cross apply (
		select stuff((
			select ',' + convert(varchar,(case 
				when m1.sn - 1 < a.w  -- 这里注意要有一个减 1 哦
				then convert(int,m1.match) -- 承重小于当前物品重量,继承上一次的价值数
				else -- 否则,该格子的价值,应该为上次价值数和加入该物品价值后的较大的那一个
					(case 
					when m2.rp + a.p > convert(int,m1.match) 
					then m2.rp + a.p 
					else convert(int,m1.match) 
					end)
				end))
			from master..RegexMatches(cols,'\d+') m1 -- 老顾这里是用正则切割的字符串,有需要正则的,可以查阅老顾以前的 sql 文章,或者在评论区扣老顾
			outer apply ( -- 再次切割一次,按照背包计算的方式,使承重减去当前物品重量,与原有承重价值对齐,方便计算当前物品加入后的价值和原价值
				select isnull(convert(int,match),0) rp 
				from master..regexmatches(cols,'\d+')
				where m1.sn - a.w = sn
			) m2
			order by m1.sn
			for xml path('')
		),1,1,'') ncols
	) b
)
select * from t2
order by rid

在这里插入图片描述
这个时候,我们只需要拿到最后一行数据中的 cols 中的最后一个数据,那就是最大价值了。

对 for xml 的结果进行引用时的处理

这里再补充一下,老顾用正则切出来的数据格式

select * from master..regexmatches('0,0,0,0,0,0,0,0,0,0,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12','\d+')
sn          match      index
----------- ---------- -----------
1           0          0
2           0          2
3           0          4
4           0          6
5           0          8
6           0          10
7           0          12
8           0          14
9           0          16
10          0          18
11          12         20
12          12         23
13          12         26
14          12         29
15          12         32
16          12         35
17          12         38
18          12         41
19          12         44
20          12         47
21          12         50
22          12         53
23          12         56
24          12         59
25          12         62
26          12         65
27          12         68
28          12         71
29          12         74
30          12         77
31          12         80
32          12         83
33          12         86
34          12         89
35          12         92
36          12         95
37          12         98
38          12         101
39          12         104
40          12         107
41          12         110

(41 行受影响)

这次背包问题中,只用到了前两列,即sn和match列,用 string_split 的小伙伴,只需要生成两列的数据,分别表示 number 和 cp 列即可,老顾在递归里没有改列名,直接使用了,小伙伴可以自行修改,即可得到结果了。

完全背包

完全背包问题和 01 背包问题的区别在于,01 背包中,每个物品只能用 1 次,而完全背包则不限制数量,那么完全背包的问题可以转化成 01 背包,直接在 cte 里插入一个数据扩展部分即可。


declare @place int
set @place = 40

;with t as (
	select 'A' name,10 w,12 p
	union all select 'B',12,16
	union all select 'C',11,16
	union all select 'D',16,24
	union all select 'E',10,15
),tf as (  -- 插入一个临时表,用来计算如果承重完全放同一种物品时,每个物品最多能放多少个
	select a.* 
	from t a
	cross apply (
		select number
		from master..spt_values
		where number<=(@place/w) and type='p'
	) b
),t1 as ( -- 排序的数据来源,从原始表变成扩展表
	select *,row_number() over(order by name) rid from tf
),t2 as (
	select * 
	from t1 
	cross apply (
		select stuff((
			select ',' + convert(varchar,(case when number < w then 0 else p end))
			from master..spt_values 
			where type ='p' and number<=@place
			order by number
			for xml path('')
		),1,1,'') cols
	) b
	where rid=1
	union all
	select name,w,p,rid,ncols 
	from (
		select a.*,cols
		from t1 a,t2 b 
		where b.rid+1=a.rid
	) a
	cross apply (
		select stuff((
			select ',' + convert(varchar,(case 
				when m1.sn - 1 < a.w 
				then convert(int,m1.match)
				else
					(case 
					when m2.rp + a.p > convert(int,m1.match) 
					then m2.rp + a.p 
					else convert(int,m1.match) 
					end)
				end))
			from master..RegexMatches(cols,'\d+') m1
			outer apply (
				select isnull(convert(int,match),0) rp 
				from master..regexmatches(cols,'\d+')
				where m1.sn - a.w = sn
			) m2
			order by m1.sn
			for xml path('')
		),1,1,'') ncols
	) b
)
select * from t2
order by rid


多重背包

。。。。。。我连完全背包都弄出来了,你问我多重背包?

多重背包的问题和完全背包又有一点点区别,就是每个物品有最大数量。。。。

得,这个问题老顾就不解答了,直接修改 cte 表 t 的部分,将数量给出,然后 tf 中按这个数量扩展即可。

小结

老顾就是闲的,嘿嘿,至于更多的背包问题结果,比如选中了哪些物品之类的,老顾这里就不再列出了,有兴趣的小伙伴可以自行解决了哦。

我们这次因为有了上一次监考安排的经验,所以这次还是很顺利的就完成了,可喜可贺可喜可贺。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/410692.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

leetcode:快乐数(详解)

前言&#xff1a;内容包括&#xff1a;题目&#xff0c;代码实现&#xff0c;大致思路&#xff0c;代码解读 题目&#xff1a; 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字…

坚鹏:《银行业数字化转型指导意见》政策解读及银行数字化转型

中国银保监会《关于银行业保险业数字化转型的指导意见》政策解读及银行数字化转型课程背景&#xff1a; 很多银行存在以下问题&#xff1a; 不知道如何准确理解中国银保监会《关于银行业保险业数字化转型的指导意见》相关政策 不清楚中国银保监会《关于银行业保险业数字化转型…

使用AI进行“文本纠错”

AI在现实中的应用有很多&#xff0c;你有没有想过&#xff0c;它还可以进行文本纠错呢&#xff1f;传统的校对既耗时又枯燥&#xff0c;通过“AI纠错”&#xff0c;不仅能更快完成&#xff0c;还能提高准确度。那么AI“文本纠错”背后的原理是什么呢&#xff1f;和我一起看看吧…

Python综合案例-小费数据集的数据分析(详细思路+源码解析)

目录 1. 请导入相应模块并获取数据。导入待处理数据tips.xls&#xff0c;并显示前5行。 2、分析数据 3.增加一列“人均消费” 4查询抽烟男性中人均消费大于5的数据 5.分析小费金额和消费总额的关系&#xff0c;小费金额与消费总额是否存在正相关关系。画图观察。 6分析男女顾…

chatGPT写文章攻略-用chatGPT写网文

chatGPT可以写中文吗 ChatGPT可以写中文。在过去的几年中&#xff0c;许多深度学习机器翻译模型已经开始探索中英文翻译、去噪声、语音识别等任务&#xff0c;并且在这些任务中ChatGPT具有最先进的表现。 目前&#xff0c;例如GPT-3和GPT-2都可以用来生成中文文本。为此&…

以太坊上海升级,DeFi 3.0的序章

引言 距离以太坊Shapella升级&#xff08;也曾被称为上海升级&#xff09;仅剩一天的时间&#xff01;自2015年以太坊上线以来&#xff0c;它已成为世界排名第二的加密虚拟资产&#xff0c;以太坊诞生于行业的意义在于它能够让开发者构建智能合约和去中心化应用&#xff08;DAp…

Jumpserver与Freeipa集成(以及其他配置)

背景&#xff1a; jumpserver的安装参照&#xff1a;jumpserver的简单安装使用&#xff0c;Freeipa的安装参照&#xff1a;Freeipa的简单搭建配置。准备将Freeipa与Jumpserver集成。其实Freeipa搭建后linux客户端如果安装了Freeipa client。也能完成用户的授权权限管理了&…

肖 sir_就业课__005项目数据

项目数据 一、项目周期 &#xff08;1&#xff09;新项目&#xff1a;从无到有&#xff0c;从项目的开始到上线的时间 时间长&#xff1a;3个月、6个月、1年、2年 &#xff08;2&#xff09;老项目&#xff1a;迭代项目 迭代周期&#xff1a;1个月、2个月、3个月迭代、 &#…

现在是香港推动Web3的“正确时机” 将采取监管与发展并重策略

香港财政司司长陈茂波在香港特区政府网站发表司长随笔《发展Web3—守正创新 稳慎前行》&#xff0c;提出为了让Web3稳慎走好创新发展的道路&#xff0c;政府将采取“适当监管”和“推动发展”两者并重的策略&#xff0c;确保虚拟资产行业可持续和负责任发展。 此前&#xff0c;…

java_集合

1.集合 集合分为单列集合&#xff08;collection&#xff09;和双列集合&#xff08;map&#xff09;. 单列就是每个位置只有一个值&#xff0c;双列则是每个位置都是一对键值对&#xff0c;类似于python的字典。 2.collection 其中&#xff0c;collection又可以分为List…

全国大学生智能汽车竞赛——安装Ubuntu操作系统(双系统)

1.1 电脑分区 1.1.1 分区原因 由于我们想要在电脑上同时安装Windows和Ubuntu系统&#xff0c;所以就要在window使用的内存中划分出来一段用来给Ubuntu系统使用&#xff0c;相当于一个应用程序一样 1.1.2 分区步骤 1.右击此电脑&#xff0c;点击管理&#xff0c;然后双击左侧…

【刷题笔记】--dp--376. 摆动序列122. 买卖股票的最佳时机 II

感觉自己dp还不是很会&#xff08;/(ㄒoㄒ)/~~ 写dp题的步骤&#xff1a;①通过定义子问题&#xff0c;确定dp[ ] or dp[ ][ ] 表示的含义 ②写出子问题的递归关系 ③确定初始条件 题目&#xff1a; 思路&#xff1a; ①确定dp的含义&#xff1a;dp[i]表示 到i位置&#x…

网络信息安全(三层设备部署DHCP服务器与DHCP中继、ICMP协议)

文章目录三层路由器部署DHCP服务器配置trunkVLAN创建将端口加入对应vlan路由创建子接口路由器创建地址池服务器配置静态IPDHCP服务器部署创建作用域在三层设备配置DHCP中继DHCP中继原理ICMP协议概述用途封装格式三层路由器部署DHCP服务器 配置trunk VLAN创建 这里以S1为例&…

Vulnhub:Digitalworld.local (Development)靶机

kali&#xff1a;192.168.111.111 靶机&#xff1a;192.168.111.130 信息收集 端口扫描 nmap -A -v -sV -T5 -p- --scripthttp-enum 192.168.111.130 查看网站首页源码 访问development目录&#xff0c;提示存在一个流量包 查看流量包发现另一个网站路径&#xff1a;/devel…

多模态之论文笔记ViLT

文章目录ViLT: Vision-and-Language Transformer Without Convolution or Region Supervision一. 简介1.1 摘要1.2 文本编码器&#xff0c;图像编码器&#xff0c;特征交互复杂度分析1.2 特征交互方式分析1.3 图像特征提取分析二. 方法 Vision-and-Language Transformer2.1. 方…

【高危】Apache Linkis JDBC EngineConn 插件<1.3.2 存在反序列化漏洞

漏洞描述 Apache Linkis 是一个用于将上层应用与底层数据引擎解耦&#xff0c;提供标准化接口的中间件。 该项目受影响版本存在反序列化漏洞&#xff0c;由于ConnectionManager.java中未对dbUrl、username、password等参数进行充分过滤&#xff0c;当恶意用户完全控制应用程序…

缘起|蚂蚁应用级服务发现的实践之路

文&#xff5c;肖健&#xff08;花名&#xff1a;昱恒&#xff09;蚂蚁集团技术专家、SOFARegistry Maintainer专注于服务发现领域&#xff0c;目前主要从事蚂蚁注册中心 SOFARegistry 的设计和研发工作。本文 8339 字 阅读 15 分钟PART. 1前言什么是服务发现&#xff1f;我们今…

Python人工智能在气象中的实践技术应用

专题一 Python 和科学计算基础 1.1 Python 入门和安装 1.1.1 Python 背景及其在气象中的应用 1.1.2 Anaconda 解释和安装以及 Jupyter 配置1.1.3 Python 基础语法 1.2 科学数据处理基础库 1.2.1 Numpy 库1.2.2 Pandas 库1.2.3 Scipy 库 1.2.4 Matplotlib 和 Cartopy 库 …

thinkphp6多应用模块配置使用

1.安装thinkphp6 6.0之前安装都是从Git或者直接下载安装包进行安装&#xff0c;从6.0开始安装方式就改成了composer进行安装了&#xff0c;安装命令如下&#xff1a; 稳定版&#xff1a;composer create-project topthink/think tp 开发版&#xff1a;composer create-projec…

SOLIDWORKS官方认证考试

SOLIDWORKS官方认证考试&#xff1a; CSWA认证 CSWP认证 CSWPA认证 CSWE认证 SOLIDWORKS认证考试是达索SOLIDWORKS公司推出的全球性认证考试项目&#xff0c;是作为衡量您所具备的SOLIDWORKS应用专长与能力的一种测试和认可&#xff0c;主要考察对设计、仿真一体化、机电一体…