2021-03-01

蓝桥杯对局匹配算法解析

找不到错误在哪,求

蓝桥杯历届真题 对局匹配

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

struct node
{
	int val;
	int sum;
}e[100005];

int main()
{
	int n, k;
	int t;
	int box[100005] = {0};
	int f[100005] = {0};
	cin >> n >> k;
	int mx = 0;
	for(int i = 0 ; i < n ; i ++)
	{
		cin >> t;
		box[t]++;
		mx = max(mx , t);
	}	
	
	if(k == 0)
	{
		int ans = 0;
		for(int i = 1 ; i <= mx ; i ++)
			if(box[i])ans ++;
		cout << ans;
	}
	else
	{
		int z = 1;
		for(int i = 1 ; i <= mx ; i ++)
		{
			if(box[i])
			{
				int m = i;
				while(m <= 100000 && box[m])
				{
					e[z].val = m;
					e[z ++].sum = box[m];
					box[m] = 0;
					m += k;
				}
			}
		}
		
		//for(int i = 1 ; i < z; i ++)
		//	cout << e[i].val << " : " << e[i].sum <<endl;
		f[1] = e[1].sum;
		for(int i = 2 ; i < z; i ++)
		{
			if(e[i].val - e[i - 1].val == k)
				f[i] = max(f[i - 1] , f[i - 2] + e[i].sum);
			else 
				f[i] = f[i - 1] + e[i].sum;	
		//	cout << i << " : " << f[i] << endl;
		}
		cout << f[z - 1];
	}
} 
<think>我们有一个表question5_1,包含两列:客户(customer)和交易日期(trade_date)。 目标:转换为每个日期(trade_date)、每个客户(customer),以及一个标识(是否连续三个月交易)的数据形式。 注意:"是否连续三个月交易"的意思是:对于当前行(某个客户在某一天交易),判断该客户是否在接下来的三个月内每个月都有交易(即连续三个月都有交易记录)。 这里需要注意:连续三个月指的是自然月,比如2023-01-15,那么连续三个月就是2023-02、2023-03、2023-04。但是交易日期可能不是连续的,我们只需要每个月至少有一次交易就算。 思路: 1. 首先,我们需要为每个客户生成每个月的记录(因为最终输出是按月的,而不是按天)。但我们原始数据是按天的,所以第一步需要将交易日期转换为月份(截断到月),并去重(每个客户每个月只保留一条记录,因为我们关心的是每个月是否有交易)。 2. 然后,使用开窗函数,按客户分组,按月份排序,然后计算接下来的两个月的月份(通过偏移)是否有记录。 但是,问题要求输出的是“日期”(trade_date)?这里注意:原始表是按天的,但转换后我们按月份来考虑?还是按天?根据问题描述,目标形式是“日期、客户、是否连续三个月交易”。这里日期是指原始交易日期吗?但是连续三个月是一个时间段,所以对于同一个客户,在连续三个月的每个月中,每一天都会标记为“是”吗?这似乎不太合理。 重新理解:对于原始表中的每一行(一个客户在某一天有一条交易记录),我们需要判断该客户从这一天开始,接下来的三个月(自然月)中,每个月是否都有交易(即每月至少一次交易)。注意,这里是以每一天为基准,判断从该天所在月份开始的连续三个月。 例如:客户A在2023-01-15交易了一次,那么我们要看客户A在2023-02、2023-03、2023-04这三个月是否都有交易(至少一次)。注意:2023-01-15所在的月份是2023年1月,那么连续三个月应该是2023-01、2023-02、2023-03?还是从下一个月份开始? 题目要求“连续三个月交易”,通常是指包含当前月以及接下来的两个月?还是从当前月开始的连续三个月?还是从当前月的下一个月开始连续三个月?这里需要明确。 根据常见的业务场景(比如判断连续三个月活跃),通常是指当前月以及接下来的两个月(共三个月)。所以: 当前月:trade_date所在的月份(记为month0) 下个月:month0+1(记为month1) 下下个月:month0+2(记为month2) 那么,对于每一行,我们要判断该客户在month0、month1、month2这三个月是否都有交易记录。 然而,注意:当前行已经证明了month0有交易,所以我们只需要判断month1和month2是否有交易。 但是,问题要求输出的是原始的行(按天),所以每条记录都要判断。 步骤: 1. 首先,我们为每个客户计算每个交易日期对应的月份(trade_month),然后我们还需要知道该客户有交易的月份集合(以便判断后续月份是否存在)。 2. 然后,我们可以在原始表(按天)的基础上,为每一行(每个客户,每个交易日期)计算: 是否连续三个月交易 = 该客户在trade_date所在月份的下个月和下下个月都有交易记录吗? 但是,注意:同一个客户在同一个月份可能有多个交易日期,我们只需要判断该月份是否有交易,不需要多次判断。 因此,我们可以这样做: a. 先构建一个辅助子查询,得到每个客户每个有交易的月份(去重)。 b. 然后,在原始表的基础上,使用EXISTS或开窗函数来检查后续两个月份是否存在。 具体步骤: 方法1(使用子查询): 对于原始表t1的每一行,我们检查: 是否存在下个月的记录?即:存在一条记录(同一个客户)的交易日期所在的月份 = 当前月份+1 是否存在下下个月的记录?即:存在一条记录(同一个客户)的交易日期所在的月份 = 当前月份+2 但是,这样效率可能不高,而且需要多次关联。 方法2(使用开窗函数): 我们可以先构建每个客户每个月的交易记录(一行代表一个月),然后在这个按月的结果集上,使用LEAD函数来获取接下来的两个月份是否存在。 然后,再把这个按月的结果关联回原来的按天记录?但是按月的结果只有一个月一条,而按天的记录在一个月内有多条。 所以,我们可以: 步骤1:将原始表按客户和月份分组(得到客户每个月的交易月份),并标记该月份(比如用1表示有交易,NULL表示没有,但实际上我们只取有交易的月份)。 步骤2:使用LEAD函数,按客户分组,月份排序,获取接下来两个月份(按自然月顺序)的记录。如果接下来第一个月存在(即LEAD的第一行)且月份正好是当前月份+1,并且接下来第二个月份存在且正好是当前月份+2,那么当前月份就满足连续三个月(当前月,下个月,下下月)都有交易。 但是,这样得到的是按月的结果。我们需要将这个结果关联回原始表,因为原始表是按天的。 步骤3:将按月的结果(每个客户,月份,以及一个标记“是否连续三个月交易”)关联到原始表,关联条件为同一个客户,并且原始交易日期所在的月份=月份结果中的月份。 这样,原始表中的每一天都会获得它所在月份是否满足连续三个月交易(注意,同一个月份内的所有天,这个标记是一样的)。 但是,注意:我们判断的是“当前月份”是否满足连续三个月,而连续三个月是指当前月、下个月、下下月。所以,对于原始表中的每一天,我们标记的是:该天所在的月份(设为M)以及它的下个月(M+1)和下下个月(M+2)是否都有交易。 因此,对于某一天,如果它所在的月份M满足(M, M+1, M+2)都有交易,那么这一天的记录就标记为“是”,否则为“否”。 但是,这里有一个问题:如果某客户在1月有多次交易,在2月也有多次交易,在3月也有多次交易,那么该客户在1月的每一天都会被标记为“是”;在2月的每一天也会被标记为“是”(因为2月、3月、4月都要有?不对,2月的连续三个月是2月、3月、4月)?不对,对于2月的记录,它的连续三个月应该是2月、3月、4月。所以每个月的标记都是独立的。 所以,我们需要按月计算连续三个月的标记,然后按月份关联回每一天。 具体SQL如下: 步骤1:从原始表question5_1中提取每个客户每个月份(去重),并转换为月份(trunc(trade_date, 'MM')) 创建临时视图monthly_trades: SELECT customer, TRUNC(trade_date, 'MM') AS trade_month FROM question5_1 GROUP BY customer, TRUNC(trade_date, 'MM') 步骤2:使用LEAD函数,按客户分组,按月份排序,获取后面两个月份 创建临时视图连续标记: SELECT customer, trade_month, LEAD(trade_month, 1) OVER (PARTITION BY customer ORDER BY trade_month) AS next_month1, LEAD(trade_month, 2) OVER (PARTITION BY customer ORDER BY trade_month) AS next_month2 FROM monthly_trades 步骤3:然后判断:如果next_month1等于trade_month+1个月(即ADD_MONTHS(trade_month,1)),并且next_month2等于trade_month+2个月(即ADD_MONTHS(trade_month,2)),那么说明连续三个月都有交易。 注意:因为LEAD取的是实际存在的月份,所以我们只需要检查: next_month1 = ADD_MONTHS(trade_month, 1) 且 next_month2 = ADD_MONTHS(trade_month, 2) 那么,这个trade_month就满足连续三个月交易。 创建视图monthly_flag: SELECT customer, trade_month, CASE WHEN (next_month1 = ADD_MONTHS(trade_month, 1) AND next_month2 = ADD_MONTHS(trade_month, 2)) THEN '是' ELSE '否' END AS is_continuous FROM 连续标记 步骤4:将按月计算的标记关联回原始表(按天)。注意,原始表的一条记录可能有多个月份,所以我们用月份关联。 原始表:question5_1,我们提取交易日期所在的月份:TRUNC(trade_date, 'MM'),然后和monthly_flag进行关联。 最终SQL: WITH monthly_trades AS ( SELECT DISTINCT customer, TRUNC(trade_date, 'MM') AS trade_month FROM question5_1 ), lead_months AS ( SELECT customer, trade_month, LEAD(trade_month, 1) OVER (PARTITION BY customer ORDER BY trade_month) AS next_month1, LEAD(trade_month, 2) OVER (PARTITION BY customer ORDER BY trade_month) AS next_month2 FROM monthly_trades ), monthly_flag AS ( SELECT customer, trade_month, CASE WHEN next_month1 = ADD_MONTHS(trade_month, 1) AND next_month2 = ADD_MONTHS(trade_month, 2) THEN '是' ELSE '否' END AS is_continuous FROM lead_months ) SELECT q.trade_date, q.customer, mf.is_continuous AS "是否连续三个月交易" FROM question5_1 q LEFT JOIN monthly_flag mf ON q.customer = mf.customer AND TRUNC(q.trade_date, 'MM') = mf.trade_month 注意:这里我们使用了LEFT JOIN,确保原始表的每一行都会出现。如果一个月份在monthly_flag中没有(但实际不会,因为monthly_trades已经包含了所有出现的月份),那么is_continuous就会是NULL,但我们用CASE WHEN已经处理了所有情况,所以不会有NULL?不对,在lead_months中,如果一个月份的最后两个月份不存在(即没有后续两个月),那么LEAD得到的就是NULL,这时CASE WHEN就会返回'否'。 但是,这里有一个边界情况:如果客户在某个月份之后就没有交易了,那么后续两个月就不存在,所以标记为'否'。 另一种思路:我们也可以不构建按月去重的临时表,而是直接在原始表上使用开窗函数,按月份去重后使用LEAD。但是,去重是必要的,因为同一个月份多次出现会导致LEAD得到同一个月份多次。 所以,这个SQL应该能满足要求。 但是,请注意:如果同一个月份有多个交易记录,我们在monthly_trades中只保留一条,然后在关联回按天记录时,同一个月份内的所有天都会得到同一个标记。 这就是我们想要的结果。 因此,最终SQL如下: </think>要使用 Oracle 的偏移开窗函数实现需求,需按以下步骤操作: ### 解决方案 ```sql WITH MonthlyTrades AS ( SELECT customer, TRUNC(trade_date, 'MM') AS month_start, -- 按月分组 MIN(trade_date) AS min_date_in_month -- 取月份内任意日期代表该月 FROM question5_1 GROUP BY customer, TRUNC(trade_date, 'MM') ), FlaggedMonths AS ( SELECT customer, month_start, -- 检查后续两个月是否存在交易 CASE WHEN LEAD(month_start, 1) OVER (PARTITION BY customer ORDER BY month_start) = ADD_MONTHS(month_start, 1) AND LEAD(month_start, 2) OVER (PARTITION BY customer ORDER BY month_start) = ADD_MONTHS(month_start, 2) THEN '是' ELSE '否' END AS is_continuous FROM MonthlyTrades ) SELECT q.trade_date AS "日期", q.customer AS "客户", f.is_continuous AS "是否连续三个月交易" -- 包含当前月及后续两月 FROM question5_1 q JOIN FlaggedMonths f ON q.customer = f.customer AND TRUNC(q.trade_date, 'MM') = f.month_start ORDER BY q.trade_date, q.customer; ``` ### 关键说明 1. **逻辑分层处理**: - **MonthlyTrades**:将原始日粒度数据按月聚合,使用 `TRUNC(trade_date, 'MM')` 截断到月 - **FlaggedMonths**:通过 `LEAD()` 开窗函数检查后续两个月是否存在交易记录 - **最终关联**:将月级标记结果关联回原始日粒度数据 2. **偏移开窗核心**: ```sql LEAD(month_start, 1) OVER (PARTITION BY customer ORDER BY month_start) = ADD_MONTHS(month_start, 1) ``` - `LEAD(,1)` 获取下一个月份记录 - `ADD_MONTHS(,1)` 计算理论上的下个月份 - 两者相等说明下月有交易 3. **连续三个月定义**: - 包含当前月 + 后续两个月(如1月数据需验证2月、3月存在交易) - 同一客户同月内所有日期共享相同标记 ### 示例效果 原始数据: | 客户 | 交易日期 | |------|-----------| | A | 2023-01-05| | A | 2023-02-10| | A | 2023-03-15| | B | 2023-01-20| 输出结果: | 日期 | 客户 | 是否连续三个月交易 | |------------|------|-------------------| | 2023-01-05 | A | 是 | -- 因A在2/3月有交易 | 2023-01-20 | B | 否 | -- B后续月份无交易 | 2023-02-10 | A | 否 | -- A在4月无交易(需验证3/4月) | 2023-03-15 | A | 否 | -- 后续不足两月 > 注:最终月份是否标记为"是"取决于其自身及后续两个月的交易连续性验证[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值