蓝桥杯----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合法的情况下

  

  而且在写代码的情况下,如果要固定某些值

   最后不要写成下面这种:

    

 

  而且先

  

     枚举好其中的固定值

  然后函数调用,将固定值传入计算

 

posted @ 2023-05-28 20:38  次林梦叶  阅读(76)  评论(0)    收藏  举报