文章目录
- 日期统计
- 问题描述
- 答案:235
- 回溯算法
- 暴力枚举
日期统计
问题描述
小蓝现在有一个长度为 100 的数组,数组中的每个元素的值都在 0 到 9 的范围之内。数组中的元素从左至右如下所示:
5 6 8 6 9 1 6 1 2 4 9 1 9 8 2 3 6 4 7 7 5 9 5 0 3 8 7 5 8 1 5 8 6 1 8 3 0 3 7 9 2
7 0 5 8 8 5 7 0 9 9 1 9 4 4 6 8 6 3 3 8 5 1 6 3 4 6 7 0 7 8 2 7 6 8 9 5 6 5 6 1 4 0 1
0 0 9 4 8 0 9 1 2 8 5 0 2 5 3 3
现在他想要从这个数组中寻找一些满足以下条件的子序列:
- 子序列的长度为 8;
- 这个子序列可以按照下标顺序组成一个 yyyymmdd 格式的日期,并且要求这个日期是 2023 年中的某一天的日期,例如 20230902,20231223。yyyy 表示年份,mm 表示月份,dd 表示天数,当月份或者天数的长度只有一位时需要一个前导零补充。
请你帮小蓝计算下按上述条件一共能找到多少个不同的 2023 年的日期。对于相同的日期你只需要统计一次即可。
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案:235
回溯算法
这段代码实现了一个基于回溯算法的日期统计问题。下面我将对代码进行详细注释。
// 包含常用库文件和命名空间
#include<bits/stdc++.h>
using namespace std;// ans 用来记录符合条件日期的总数
int ans=0;
// path 用来保存当前正在尝试的日期序列
vector<int> path;
// month 数组用来记录每个月的天数,注意索引0是一个哑元素,从1开始代表1月
int month[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};// 回溯函数,用于生成所有可能的日期序列
void backstracking(int a[],int start)
{// 如果路径长度为8,即已经找到一个完整的日期序列if(path.size()==8){// 检查序列是否以2023年开头if(path[0]==2&&path[1]==0&&path[2]==2&&path[3]==3){// 从序列中提取出月份int mm=path[4]*10+path[5];// 检查月份是否在1到12之间if(mm>=1&&mm<=12){// 从序列中提取出日期int dd=path[6]*10+path[7];// 检查日期是否在该月可能的日期范围内if(dd>=1&&dd<=month[mm]){// 如果日期合法,计数器加1ans++;}}}// 完整的日期序列已经生成,无需进一步递归,因此返回return ;}// 使用set来避免同一层使用重复的数字unordered_set<int> used;// 从start位置开始遍历数组for(int i=start;i<100;i++){// 如果当前数字已经在本层使用过,跳过if(used.find(a[i])!=used.end())continue;// 将当前数字加入到used集合中,表示本层已使用used.insert(a[i]);// 将当前数字加入到path中path.push_back(a[i]);// 递归调用回溯函数,从下一个位置开始backstracking(a,i+1);// 回溯,从path中移除当前数字path.pop_back();}
}int main()
{// 初始化数组a,包含100个0到9的数字int a[100]={5,6,8,6,9,1,6,1,2,4,9,1,9,8,2,3,6,4,7,7,5,9,5,0,3,8,7,5,8,1,5,8,6,1,8,3,0,3,7,9,2,
7,0,5,8,8,5,7,0,9,9,1,9,4,4,6,8,6,3,3,8,5,1,6,3,4,6,7,0,7,8,2,7,6,8,9,5,6,5,6,1,4,0,1,
0,0,9,4,8,0,9,1,2,8,5,0,2,5,3,3};// 从数组第0个位置开始回溯backstracking(a,0);// 输出找到的符合条件的日期总数cout<<ans;return 0;
}
这段代码通过深度优先搜索的回溯方法,尝试所有可能的8位数日期序列,并检查它们是否符合条件。每当找到一个有效的日期时,会将计数器ans
增加1。最后,程序输出这个计数器,即符合条件的日期数量。
暴力枚举
此段代码的目的是从一个固定的、包含100个0到9数字的数组中寻找所有符合特定日期格式(即2023年的有效日期)的子序列,并统计这些不同有效日期的数量。以下为详细注释:
// 包含常用的头文件和使用标准命名空间
#include<bits/stdc++.h>
using namespace std;// 定义month数组,用于存储各月的天数,考虑到不是闰年,所以二月是28天
int month[13]={0,31,28,31,30,31,30,31,31,30,31,30,31}; // 索引0是占位符
int months[13]; // 用于标记某年某月是否已经被使用int main()
{// a数组包含了100个数字,代表原题中给出的数列int a[100]={5,6,8,6,9,1,6,1,2,4,9,1,9,8,2,3,6,4,7,7,5,9,5,0,3,8,7,5,8,1,5,8,6,1,8,3,0,3,7,9,2,
7,0,5,8,8,5,7,0,9,9,1,9,4,4,6,8,6,3,3,8,5,1,6,3,4,6,7,0,7,8,2,7,6,8,9,5,6,5,6,1,4,0,1,
0,0,9,4,8,0,9,1,2,8,5,0,2,5,3,3};// 使用set集合res来存储不同的日期,利用集合的特性自动去重set<int> res;// 以下循环结构嵌套层层筛选符合条件的子序列// 第一层循环,从第一个数字开始,寻找'2',代表年份的千位for(int i=0;i<100-7;i++) // 保证后面有足够的数字组成完整日期{if(a[i]==2){// 第二层循环,寻找'0',代表年份的百位for(int j=i+1;j<100-6;j++){if(a[j]==0){// 第三层循环,寻找'2',代表年份的十位for(int k=j+1;k<100-5;k++){if(a[k]==2){// 第四层循环,寻找'3',代表年份的个位for(int l=k+1;l<100-4;l++){if(a[l]==3){// 第五层循环,寻找小于'2'的数,代表月份的十位for(int o=l+1;o<100-3;o++){if(a[o]<2){// 第六层循环,寻找月份的个位for(int p=o+1;p<100-2;p++){// 组成月份并检查是否合法int mm=a[o]*10+a[p];if(mm>=1&&mm<=12&&months[mm]==0){// 标记该月份已被使用months[mm]=1;// 第七层循环,寻找天数的十位for(int t=p+1;t<100-1;t++){if(a[t]<4){// 第八层循环,寻找天数的个位for(int y=t+1;y<100;y++){// 组成天数并检查是否合法int dd=a[t]*10+a[y];if(dd>=1&&dd<=month[mm]){// 将合法的日期加入集合中res.insert(mm*100+dd);}}}}}}}}}}}}}}}}// 输出不同有效日期的数量cout<<res.size();return 0;
}
这段代码使用了八层嵌套循环来穷举所有可能的日期,每一层循环都是在寻找日期的下一位数字。它使用了一个set
来保存所有合法的日期,这样可以自动处理重复日期的问题。程序最终输出了这个集合的大小,即有效日期的总数。注意,程序中的months
数组用来标记2023年的每个月是否已经被检查过。