您现在的位置是:首页 >技术交流 >2023第十四届蓝桥杯国赛 C/C++ 大学 B 组网站首页技术交流
2023第十四届蓝桥杯国赛 C/C++ 大学 B 组
省赛还水了个省一,国赛原型毕露了
参考文献:(13条消息) 2023第十四届蓝桥杯国赛 C/C++ 大学 B 组_旧林墨烟的博客-CSDN博客
(13条消息) 2023第十四届蓝桥杯国赛 C/C++ 大学 B 组 (赛后记录)_.Zero的博客-CSDN博客
A:子2023
【问题描述】
小蓝在黑板上连续写下从 1 到 2023 之间所有的整数,得到了一个数字序列:
S = 12345678910111213 . . . 20222023。
小蓝想知道 S 中有多少种子序列恰好等于 2023?
提示,以下是 3 种满足条件的子序列(用中括号标识出的数字是子序列包含的数字):
1[2]34567891[0]111[2]1[3]14151617181920212223…
1[2]34567891[0]111[2]131415161718192021222[3]…
1[2]34567891[0]111213141516171819[2]021222[3]…
注意以下是不满足条件的子序列,虽然包含了 2、0、2、3 四个数字,但是顺序不对:
1[2]345678910111[2]131415161718192[0]21222[3]…
暴力枚举 +剪枝 (考试的时候怎么就没敢做哪?)
答案:5484660609
#include<bits/stdc++.h>
using namespace std;
const int MAX=2e5+10;
typedef long long ll;
#define x first
#define y second
typedef pair<int,int>PII;
ll sum,cnt,target;
ll dp[2500][20];
int main()
{
string s;
for(int i=1;i<=2023;i++)
{
int x=i;
string t=to_string(i);
for(int j=0;j<t.size();j++)
{//光把数字中的2/3/0加进来,剪枝
if(t[j]=='2'||t[j]=='3'||t[j]=='0')s+=t[j];
}
}
ll cnt=0;
int n=s.size();
cout<<s<<endl;
for(int i=0;i<n;i++)
{
if(s[i]=='2')
{
for(int j=i+1;j<n;j++)
{
if(s[j]=='0')
{
for(int k=j+1;k<n;k++)
{
if(s[k]=='2')
{
for(int p=k+1;p<n;p++)
{
if(s[p]=='3')cnt++;
}
}
}
}
}
}
}
cout<<cnt;
return 0;
}
dp 上面链接大佬的思路,太牛了
当遇到字符 ‘2’ 的时候字符串 “2” 的数量+1,字符串 “202” 的数量加上字符串 “20” 的数量
当遇到字符 ‘0’ 的时候字符串 “20” 的数量加上字符串 “2” 的数量
当遇到字符 ‘3’ 的时候字符串 “2023” 的数量加上字符串 “202” 的数量
最后 “2023” 的数量就是答案
#include<bits/stdc++.h>
using namespace std;
const int MAX=2e5+10;
typedef long long ll;
#define x first
#define y second
typedef pair<int,int>PII;
ll sum,cnt,target;
ll dp[4]; //dp四个分别代表 '2' ,'20' ,'202' ,'2023'的数量
int main()
{
string s;
for(int i=1;i<=2023;i++)
{
int x=i;
string t=to_string(i);
for(int j=0;j<t.size();j++)
{//光把数字中的2/3/0加进来,剪枝
if(t[j]=='2'||t[j]=='3'||t[j]=='0')s+=t[j];
}
}
int n=s.size();
for(int i=0;i<n;i++)
{
if(s[i]=='2')
{
dp[0]++;//'2'数量++
dp[2]+=dp[1]; //进来一个2,'202'数量加上'20'的数量
}
else if(s[i]=='0')
{
dp[1]+=dp[0];//进来一个0,'20'数量加上‘2’的数量
}
else if(s[i]=='3')
{
dp[3]+=dp[2]; //进来一个3,'2023'数量加上'202'数量
}
}
cout<<dp[3];
return 0;
}
B: 双子数
【问题描述】
若一个正整数 x 可以被表示为 p2 × q2,其中 p、q 为质数且 p , q,则 x 是一个 “双子数”。请计算区间 [2333, 23333333333333] 内有多少个 “双子数”?
考试的时候暴力没搜出来-- 以后要习惯暴力找剪枝
long long 开不下prime[i]*prime[i]*prime[j]*prime[j] 错误结果是 947293
要用__int128
答案 947293
#include<bits/stdc++.h>
using namespace std;
const int MAX=2e5+10;
typedef long long ll;
#define x first
#define y second
#define int __int128
typedef pair<int,int>PII;
ll sum,cnt,target;
bool isPrime(ll x)
{
if(x==2||x==3||x==5)return true;
for(ll i=2;i<=sqrt(x);i++)
{
if(x%i==0)return false;
}
return true;
}
signed main()
{
vector<int>prime;
ll left=2333,right=23333333333333;
for(int i=2;i<=sqrt(right);i++)
{//不会用筛 就暴力了
if(isPrime(i))
prime.push_back(i);
}
cout<<"hehe
";
for(int i=0;i<prime.size();i++)
{
for(int j=i+1;j<prime.size();j++)
{
int res=prime[i]*prime[i]*prime[j]*prime[j];
if(res>=left&&res<=right)cnt++;
if(res>right)break;//大于直接退出 ,后面的更大,所以后面没答案 ——剪枝
}
}
cout<<cnt;
return 0;
}
C题 班级活动
找规律
sum1存人数多于两个的人数去掉匹配的 2 位剩下的总人数 下面记作集合S
sum2存储出现一次的, 这是单个的需要处理的 下面记作集合V
如果sum1>=sum2 res=sum1 把集合S中的和V中的匹配sum1-sum2个,剩下S的sum2个两两匹配成其他未出现的数
例子 1 1 1 1 1 2 2 2 3 4
需处理的是 1 1 1 2, 3 4 把左边任意两个和右边两个匹配(1 1 便 3 4),左边剩下两个随便换一个(1 2 换 6 6(没出现的都行))
如果sum1<sum2 S和V所有匹配,V剩下的两两匹配
1 1 1 2 2 2 2 3 4 5 6 7
需要处理 1 2 2 ,3 4 5 6 7
把左边三个和右面三个匹配(1 2 2 变为 3 4 5),右面剩下两个自己匹配 (6 7 变 6 6)
#include<bits/stdc++.h>
using namespace std;
const int MAX=2e5+10;
#define x first
#define y second
#define int long long
typedef pair<int,int>PII;
signed main()
{
int n;
cin>>n;
map<int,int>mymap;
for(int i=0;i<n;i++)
{
int x;
cin>>x;
mymap[x]++;
}
int sum1=0,sum2=0;
/*sum1存人数多于两个的人数去掉匹配的 2 位剩下的总人数 下面记作集合S
sum2存储出现一次的, 这是单个的需要处理的 下面记作集合V
如果sum1>=sum2 res=sum1 把集合S中的和V中的匹配sum1-sum2个,剩下S的sum2个两两匹配成其他未出现的数
例子 1 1 1 1 1 2 2 2 3 4
需处理的是 1 1 1 2, 3 4 把左边任意两个和右边两个匹配(1 1 便 3 4),左边剩下两个随便换一个(1 2 换 6 6(没出现的都行))
如果sum1<sum2 S和V所有匹配,V剩下的两两匹配
1 1 1 2 2 2 2 3 4 5 6 7
需要处理 1 2 2 ,3 4 5 6 7
把左边三个和右面三个匹配(1 2 2 变为 3 4 5),右面剩下两个自己匹配 (6 7 变 6 6)
*/
for(auto it:mymap)
{//遍历出现过的所有数字
if(it.second>=2)sum1+=it.second-2;
else sum2+=it.second;
}
if(sum1>=sum2)cout<<sum1<<endl;
else cout<<(sum1+(sum2-sum1)/2);
return 0;
}
D题 合并数列
贪心 ——小的合并 (用队列模拟)
#include<bits/stdc++.h>
using namespace std;
const int MAX=2e5+10;
#define x first
#define y second
#define int long long
typedef pair<int,int>PII;
signed main()
{
int n,m;
cin>>n>>m;
queue<int>q1;
queue<int>q2;
int v;
for(int i=0;i<n;i++)
{
cin>>v;
q1.push(v);
}
for(int i=0;i<m;i++)
{
cin>>v;
q2.push(v);
}
int cnt=0;
while(q1.size()&&q2.size())
{
int top1=q1.front();
int top2=q2.front();
if(top1==top2)
{//相等都出队
q2.pop();
q1.pop();
}
else if(top1<top2)
{//top1小 所以q1前两个合并
q1.pop();
q1.front()+=top1;
cnt++;
}
else
{//top2小 所以q2前两个合并
q2.pop();
q2.front()+=top2;
cnt++;
}
}
cout<<cnt<<endl;
return 0;
}