蓝桥杯练习笔记(九)
一、
测试用例:
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
- 看到一个难得用DFS而不是暴力求解的题解:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
int sq[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};
int ans=0;
int datesq[8]; /* 遍历2023年365个日期,将每个日期(8个数字)存入数组中,并试图在sq表里对应寻找 */
int day[13]={0,31,28,31,30,31,30,31,31,30,31,30,31}; /* 1-12序号是对应月份的对应天数 */
/* dfs深搜的作用是,将存入到datesq里的日期,从第一位数字开始依次往后,在sq表里找相同数字 */
int dfs(int sq[], int datesq[], int pos1, int pos2)
{
if(pos2==8){ /* 当datesq的位置到第八位时,即数组序号0-7已全部找到,即那个日期的每位数组已经找到*/
return 1;
}
if(pos1>=100){
return 0; /* 当在sq表查找到100还没有pos2==8时,即那个日期找不到*/
}
if(sq[pos1]==datesq[pos2]){
dfs(sq,datesq,pos1+1,pos2+1); /* 若找到对应数字,开始寻找日期下一位,sq继续遍历 */
}
else{
dfs(sq,datesq,pos1+1,pos2); /* 没找到对应数字,继续寻找日期这一位但sq继续遍历 */
}
}
int main(int argc, char *argv[])
{
datesq[0]=2,datesq[1]=0,datesq[2]=2,datesq[3]=3;
int temp1,temp2,temp3,temp4;
int mm,dd,yyyy;
int i,j,x,y;
for(mm=1;mm<=12;mm++){
temp1=mm/10; /* 分解出月份包含的两个数字 */
temp2=mm%10;
datesq[4]=temp1,datesq[5]=temp2;
for(dd=1;dd<=day[mm];dd++){
x=0,y=0;
temp3=dd/10; /* 分解出日期所包含的两个数字 */
temp4=dd%10;
datesq[6]=temp3,datesq[7]=temp4; /* 将日期存入datesq数组里*/
if(dfs(sq,datesq,x,y)){
ans++;
}
}
}
printf("%d",ans);
return 0;
}
这里的思想是将这一年的日期都枚举一遍,然后再在线地对每个日期进行DFS搜索。
- 暴力解法:
#include <stdio.h>
int main() {
int array[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
};
int daysInMonth[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int ans = 0;
for (int month = 1; month <= 12; ++month) {
for (int day = 1; day <= daysInMonth[month]; ++day) {
int dateSeq[8] = {2, 0, 2, 3, month / 10, month % 10, day / 10, day % 10};
int k = 0;
for (int i = 0; i < 100; ++i) {
if (array[i] == dateSeq[k]) {
++k;
if (k == 8) {
ans++;
break;
}
}
}
}
}
printf("%d\n", ans);
return 0;
}
- 还有使用逆向思维的:判断数列中是否含有被查找的日期
a='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'
ans=0
def jug(x):
x1=x%100
x2=(x//100) % 100
if(x1>31 or x1<1):
return False
if(x2==4 or x2==6 or x2==9 or x2==11):
if(x1 >30):
return False
if(x2==2 and x1>28):
return False
return True
for i in range(20230101,20231232):
if(jug(i)):
tmp,chk=0,0
for j in a:
if(j==str(i)[tmp]):
tmp+=1
if(tmp>7):
chk=1
break
if(chk==1):
ans+=1
continue
print(ans)
二、
- 动态规划题解:
#include <iostream>
#include <cstring>
using namespace std;
int dp[31][100]; //dp[i][j]表示做完第i题,得分为j的方案数
int main(){
int res=0;
dp[1][0]=dp[1][10]=1;
for(int i=2 ; i<=30 ; i++)
for(int j=0 ; j<=90 ; j+=10){ //这里不枚举到100的原因是,这种情况不存在,因为是以70分结束的,100分一定会结束,而80,90分不一定结束
if(j==0){
//第i题未做对,方案数为做完第i-1题所有方案数之和
for(int k=0 ; k<=90 ; k+=10)
dp[i][0] += dp[i-1][k];
}
else{
dp[i][j] = dp[i-1][j-10]; //第i题做对
if(j == 70) res+=dp[i][j];
}
}
cout<<res;
return 0;
}
- 还有用记忆化搜索的:
#include <bits/stdc++.h>
using namespace std;
vector<vector<int>> mp(35, vector<int>(110, -1));
int dfs(int i, int score){
if(i < 0 || score == 100)return 0;
if(mp[i][score] != -1)return mp[i][score];
if(score == 70) return mp[i][score] = dfs(i - 1, 80) + dfs(i - 1, 0) + 1;
return mp[i][score] = dfs(i - 1, score + 10) + dfs(i - 1, 0);
}
int main()
{
cout<<dfs(30, 0);
return 0;
}
记忆化搜索相较于动态规划而言思维难度有所上升。
能动态规划解决的问题记忆化搜索都能解决,但反过来不一定。因为记忆化搜索的本质就是将深度优先搜索的过程,通过避免重复计算同一个状态的结果,从而将时间复杂度优化到多项式复杂度。
三、
- 暴力模拟:
#include <iostream>
using namespace std;
#include<math.h>
typedef long long ll;
int main()
{
int n = 23333333;
// 请在此输入您的代码
for (int i = 1;i < n;i++)
{
double x0 = i * 1.0 / n;
double x1 = (n - i) * 1.0 / n;
double ans = 0;
ans -= i * x0 * log2(x0) + (n - i) * x1 * log2(x1);
if (fabs(ans - 11625907.5798) < 0.0001)
{
cout << i << endl;
break;
}
}
return 0;
}
abs()函数是对整数取绝对值的函数,对浮点数取绝对值的函数是fabs()
对于定值的模拟可以用这种极限的思想取模拟,作差小于0.0001就认为相等。
四、
- 这里放上官网排在第一的题解:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
//获取[1...x]中 4的倍数 和 奇数的个数,前者下取整,后者上取整
LL get_sum(LL x)
{
return x / 4 + (x + 1) / 2;
}
int main()
{
LL l, r;
cin >> l >> r;
cout << get_sum(r) - get_sum(l - 1); //减去集合重合的部分即可
return 0;
}
这里要先进行数学上的化简才能进行处理。(题解主人:Tomato)(蓝桥官网)
五、
注意这个题不是用动态规划,因为它这个不是算子序列的问题,只是伪装起来的模拟类问题。因为子序列内部的位置也能换的,换了之后是算成两个不同的情况,但这个题里只要位置和长度一样就统统只算一种情况。
所以直接模拟就好,我们找到规律,只要子序列里左边的数大于右边的数,那么交换之后就一定能够满足题意。
#include <iostream>
#include <cstring>
using namespace std;
int cnt=0;
string a;
/*'9'依然比‘3’大 /*双指针法*/
void pan(int i,int j) {
while(a[i]==a[j]&&i<j) {
i++;
j--;
}
if(i>=j)return;
if(a[i]>a[j])cnt++;
return;
}
int main() {
getline(cin,a);
int n=a.size();
for(int i=0; i<n-1; i++) {
for(int j=i+1; j<n; j++) {
pan(i,j);
}
}
cout<<cnt;
return 0;
}