蓝桥杯----2022国C
《斐波那契与 7》
写的时候第一次尝试了暴力,跑了一个小时多都没有跑完
查了一下,大概1s可以跑1e8条指令
如果真要跑的话 202202011200 ,应该跑到比赛结束应该内跑完(希望电脑不会炸)
暴力还是不合理的,遇到这种情况试一下循环节
对于斐波那契数列Fn=Fn-1+Fn-2
所以只要出现了 (a,b)在前面出现过,那么就有循环了
其中我在使用map的时候有几个注意点:
1.count()的使用方法
count()是用来查看key出现在map中的次数
如果map<int,int>mp
则mp.count(key)
2.一般不管map定义为全局还是局部,对于未初始化的,其自动都为0(在value的类型为ll,int时)
《小蓝做实验》
这道题没啥,就是给个文件,我直接灵魂发问:c++咋读写文件来着?
c++读写文件的方式<------
string filename="tar.txt";
ifstream infile;
infile.open(filename);
if (!infile){
cout<<"error"<<endl;
return 1;
}
string line;
while (getline(infile,line))
{
}
《取模》
我是真没想到这道题完全写暴力就能满分,我还各种操作然后啥也没有

正如完全暴力的方法来思考:
我们从1~m一个一个枚举数num
当存在x,y时,说明 n mod num 的结果在之前存在过了
我们知道n mod 1=0
n mod 2=0 或 1 ,但是如果n mod 2= 0 那么我们就可以不用继续下去了
因为0已经在n mod 1 的结果出现过了
同理,如果要继续下去 那么必须 n mod num =num-1
代码暴力就行
#include<iostream> #include<cstring> #include<algorithm> #include<map> using namespace std; int main() { int t; cin>>t; while (t--) { map<int,bool>mp; int n,m; cin>>n>>m; bool flag=false; for (int i=1;i<=m;i++) { if (mp[n%i]) { flag=true; break; } else mp[n%i]=true; } if (flag) cout<<"Yes"<<endl; else cout<<"No"<<endl; } return 0; }
《斐波那契数组》
暴力也可以满分,我还dp去了....
首先要知道咋暴力,由于斐波那契数组前两项决定整个数组是咋样的(我也想到了)
暴力的话可以枚举数组的前两项,然后将整个数组求出来,与输入的数组进行对比
看改动了多少,取最小的枚举即可
但是这种方法有个要考虑的地方:
枚举数组前两项的范围是多少?
由于数组元素的大小已经知道了为<=1e6,而且>=1
想要数组中的元素<=1e6,肯定前两项与n(数组长度)不会太大
同时斐波那契数列是以指数型增长的,我们可以取

其实还有一个更偷懒的方法:
因为1s大概能跑1e8时间复杂度,可以取 1e8/n
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=1e5+5;
int n,arr[N],b[N];
int main()
{
cin>>n;
for (int i=1;i<=n;i++)
cin>>arr[i];
int mx=100000000/n;
int ans=N;
for (int i=1;i<=mx;i++)
{
int cnt=0;
b[1]=b[2]=i;
if (arr[1]!=b[1]) cnt++;
if (arr[2]!=b[2]) cnt++;
for (int j=3;j<=n;j++)
{
b[j]=b[j-1]+b[j-2];
if (arr[j]!=b[j])
cnt++;
}
ans=min(ans,cnt);
}
cout<<ans<<endl;
return 0;
}
正解:

题目给我们的数列我们记为a[]
那么每一个a[i]都对应了一个a1i
我们可以通过看最多的a1i有多少个
于是我们要修改的就为n-max(a1i)
更多的细节:
首先 ai一定要整除fi ,如果不整除则一定要修改
我们求fi的时候 因为n<=1e5 而 f100 long long 就爆了
则 fi>1e6时,因为绝对不能被ai整除(因为ai<=1e6) 所以我们不用计算fi>1e6的
只要记录一些其>1e6就行
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=1e5+5,M=1e6+5;
int n,arr[N],b[N];
int cnts[M],f[N];
int main()
{
cin>>n;
for (int i=1;i<=n;i++)
cin>>arr[i];
memset(f,-1,sizeof(f));
f[1]=f[2]=1;
for (int i=3;i<=n;i++)
{
f[i]=f[i-1]+f[i-2];
if (f[i]>1e6)
{
f[i]=-1;
break;
}
}
int ans=0;
for (int i=1;i<=n;i++)
{
if (f[i]<0)
continue;
if (arr[i]%f[i]!=0)
continue;
int t=arr[i]/f[i];
cnts[t]++;
ans=max(ans,cnts[t]);
}
cout<<n-ans<<endl;
return 0;
}
《近似 GCD》

一个区间的gcd是g,那么这个区间的每个数必然都是g的倍数
那么这个题目其实就变成了问:
给定一个数组,问这个数组有多少个长度>=2的区间,其中每个元素不是g的倍数<=1?
进一步简化,我们用1表示元素不是g的倍数,0表示元素是g的倍数
那么这个问题又变成了:
给定一个数组,问这个数组中有多少个长度>=2的区间,这个区间和<=1?
我们可以用前缀和与双指针以O(n)的时间解决:
前缀和求出数组1~i的区间和
我们双指针,一个指针i固定,另一个指针j指向使得这个区间,区间和<=1的最大的下标
那么这一段由双指针确定下来的区间中 以i为开头的区间和<=1的区间有 j-i个
然后i++,换下一个开头
#include<iostream> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; int n,g; int A[100005],pre[100005]; int main() { cin>>n>>g; for (int i=1;i<=n;i++) { cin>>A[i]; if (A[i]%g==0) pre[i]=pre[i-1]; else pre[i]=pre[i-1]+1; } ll ans=0; for (int i=1,j=1;i<=n;i++) { while (j<n && pre[j]-pre[i-1]<=1) j++; if (pre[j]-pre[i-1]>1) j--; ans+=(j-i+1)-1; } cout<<ans<<endl; return 0; }
《数组个数》

这个首先我们可以求出每个ai的范围
然后dp
这个dp我用的是4维的,
看了其他人的题解我才发现我有一维是多余了,完全可以省去
dp[i][a][b][c]:表示在a[]第i个位置设置元素成功而且合法的方案数
其中下标下各个元素为:
i-1为a,i为b,i+1为c
转移为dp[i][a][b][c]+=dp[i-1][d][a][b] 在取d,a,b,c合法的情况下
其实完全可以写成:
dp[i][a][b]:表示在a[]第i个位置设置元素成功而且合法的方案数
其中下标下各个元素为:
i-1为a,i为b
转移为dp[i][a][b]+=dp[i-1][c][a],在取c,a,b合法的情况下
而且在写代码的情况下,如果要固定某些值
最后不要写成下面这种:

而且先

枚举好其中的固定值
然后函数调用,将固定值传入计算


浙公网安备 33010602011771号