• 2020-2-15模拟赛题解


    前言

    这场比赛我发挥欠佳,各种小失误不断,也需要我自己去反思吧,以后打模拟赛也要有选择性地去打,毕竟一天 (3) 场比赛肯定会有影响。

    正文

    1. 子数整数

    错点

    考试的时候蒟蒻把 No 打成了 NO

    分析

    这道题实际上就是个小模拟,您只要会 (for) 循环就可以轻松过掉此题。

    答案是直接统计一下就好了。

    我们可以直接从 1000030000 循环,算出 s1s2s3

    for(int i=10000;i<=30000;i++){
        int g=i%10,s=i/10%10,b=i/100%10,q=i/1000%10,w=i/10000%10,s1=100*w+10*q+b,s2=100*q+10*b+s,s3=100*b+s*10+g;//s1,s2,s3的计算
        if(s1%k==0&&s2%k==0&&s3%k==0)writen(i),f=1;
    }
    

    总代码

    #include <bits/stdc++.h>
    using namespace std;
    template<typename T>inline void read(T &FF){
        T RR=1;FF=0;char CH=getchar();
        for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
        for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
        FF*=RR;
    }
    template<typename T>void write(T x){
        if(x<0)putchar('-'),x*=-1;
        if(x>9)write(x/10);
        putchar(x%10+48);
    }
    template<typename T>void writen(T x){
        write(x);
        puts("");
    }
    int k,s1,s2,s3,g,s,b,q,w;
    bool f;
    int main(){
        read(k);
        for(int i=10000;i<=30000;i++){
            int g=i%10,s=i/10%10,b=i/100%10,q=i/1000%10,w=i/10000%10,s1=100*w+10*q+b,s2=100*q+10*b+s,s3=100*b+s*10+g;//s1,s2,s3的计算
            if(s1%k==0&&s2%k==0&&s3%k==0)writen(i),f=1;
        }if(f==0)puts("No");//考试时这里直接挂掉,打成了NO
        return 0;
    }
    
    

    2. 安全逃离

    错点

    蒟蒻向老师报告题目有问题后没有认真看题,结果就死了。

    分析

    做到这道题的时候,我们考到数据范围很小,考虑高复杂度的做法。

    我的做法是最暴力的做法,复杂度大概是 (O(cr^2 imes + rc^2 imes n)),面对这么小的这么小的数据,这个做法显然能过。

    我们先写一个 (check) 函数,判断此时的矩阵是否安全。

    bool check(){
        for(int i=1;i<=r;++)
            for(int j=1;j<=c;j++)
                if(a[i][j]){//此点有牛
                    int s=0;
                    for(int l=1;l<=i;l++)
                        if(a[l][j]){
                            s++;
                            break;
                        }
                    for(int l=j;l<=n;l++)
                        if(a[i][l]){
                            s++;
                            break;
                        }
                    if(s==2)return false;//如果有牛上面和右面都被堵死了,这个矩阵显然是不安全的
                }
        return true;
    }
    

    当然还有更快速地写法啦

    bool save(){
        for(int i=1;i<=k;i++){
            int flag=0;
            for(int l=x[i]-1;l>=1;l--)
                if(a[l][y[i]]){
                    flag++;
                    break;
                }
            for(int l=y[i]+1;l<=n;l++)
                if(a[x[i]][l]){
                    flag++;
                    break;
                }
            if(flag==2)return false;
        }
        return true;
    }
    

    我们再写一个函数去枚举移掉哪头牛

    void work(){
        int f=1;
        for(int i=1;i<=k;i++){
            a[x[i]][y[i]]--;
            if(save()){//如果安全
                f=0;
                write(i);//输出
                puts("");
            }
            a[x[i]][y[i]]++;
        }if(f)puts("-1");//无解
    }
    

    总代码

    #include <bits/stdc++.h>
    using namespace std;
    template<typename T>inline void read(T &FF){
        T RR=1;FF=0;char CH=getchar();
        for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
        for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
        FF*=RR;
    }
    template<typename T>void write(T x){
        if(x<0)putchar('-'),x*=-1;
        if(x>9)write(x/10);
        putchar(x%10+48);
    }
    const int MAXN=50+10,MAXK=100+10;
    int a[MAXN][MAXN],n,m,k,x[MAXK],y[MAXK];
    bool save(){
        for(int i=1;i<=k;i++){
            int flag=0;
            for(int l=x[i]-1;l>=1;l--)
                if(a[l][y[i]]){
                    flag++;
                    break;
                }
            for(int l=y[i]+1;l<=n;l++)
                if(a[x[i]][l]){
                    flag++;
                    break;
                }
            if(flag==2)return false;
        }
        return true;
    }
    void work(){
        int f=1;
        for(int i=1;i<=k;i++){
            a[x[i]][y[i]]--;//移除
            if(save()){
                f=0;
                write(i);
                puts("");
            }
            a[x[i]][y[i]]++;//注意回溯
        }if(f)puts("-1");
    }
    int main(){
        read(n);read(m);read(k);
        for(int i=1;i<=k;i++){
            read(x[i]);read(y[i]);
            a[x[i]][y[i]]++;
        }
        if(save())puts("0");
        else work();
        return 0;
    }
    

    3. 阶乘问题

    错点

    蒟蒻竟然把 (/ 10) 打成了 (\% 10)

    分析

    做法显然

    由于数字极大,我们应该先对 (1)~(n) 的这些数字把末尾的 (0) 全部删掉

    while(x%10==0)x/=10;
    

    再把乘好后的 (s) 末尾的 (0) 也都去掉。

    关于为什么末尾会有 (0),我来举个例子 (2 imes 5=10) ,所以我们还要再筛一遍 (0)

    while(s%10==0)s/=10;
    

    总代码

    #include <bits/stdc++.h>
    using namespace std;
    template<typename T>inline void read(T &FF){
        T RR=1;FF=0;char CH=getchar();
        for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
        for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
        FF*=RR;
    }
    template<typename T>void write(T x){
        if(x<0)putchar('-'),x*=-1;
        if(x>9)write(x/10);
        putchar(x%10+48);
    }
    int main(){
        int n,s=1;
        read(n);
        for(int i=1;i<=n;i++){
            int x=i;
            while(x%10==0)x/=10;
            s*=x;
            while(s%10==0)s/=10;
        }write(s);
        return 0;
    }
    

    4. 拱猪计分

    分析

    典型的大模拟。

    明白了什么叫 (1h) 读题,(0.2h) 写代码

    我们从读入开始

    读入

    for(int i=1;i<=4;i++){
        read(size[i]);
        for(int j=1;j<=size[i];j++){
            char k;int p;
            cin>>k>>p;
            if(k=='H')card[i][p]=true;
            else if(k=='S')card[i][14]=true;
                   else if(k=='D')card[i][15]=true;
                    else if(k=='C')card[i][16]=true;
        }
    }
    

    我们用 (card_{i,j}) 记录第 (i) 个人,有没第 (j) 张得分牌。

    计分

    for(int i=1;i<=4;i++){
        bool flag=true;
        for(int j=1;j<=13;j++)
            if(card[i][j]==false){
                flag=false;
                break;
            }
        if(flag){//有没有红心牌
            int ans=200;//所有红心牌以+200分计算。
            if(card[i][14]&&card[i][15])ans=500;//若S12、D11皆在吃下所有红心牌之一家,则此玩家得+500分。
            else ans=ans+card[i][14]*fraction[14]+card[i][15]*fraction[15];
            if(card[i][16])ans*=2;//若除了C10还有其它计分牌,则将其它计分牌所得分数加倍计算。
            work(ans);//输出
            putchar(32);//输出
        }else{
            bool flg=true;
            for(int j=1;j<=15;j++)
                if(card[i][j]==true){
                    flg=false;
                    break;
                }
            if(flg){
                if(card[i][16])work(50);//若持有C10的玩家只有该张牌而没有任何其它牌则得+50分
                else work(0);//若未持有这16张牌之任一张则以得零分计算。
                putchar(32);//输出
            }else{
                int ans=0;
                for(int j=1;j<=15;j++)
                    if(card[i][j])ans+=fraction[j];
                if(card[i][16])ans*=2;
                work(ans);//输出
                putchar(32);//输出
            }
        }
    }puts("");
    

    总代码

    #include <bits/stdc++.h>
    using namespace std;
    template<typename T>inline void read(T &FF){
        T RR=1;FF=0;char CH=getchar();
        for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
        for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
        FF*=RR;
    }
    template<typename T>void write(T x){
        if(x<0)putchar('-'),x*=-1;
        if(x>9)write(x/10);
        putchar(x%10+48);
    }
    void work(int x){
        if(x<0)cout<<x;
        else if(x>0)cout<<'+'<<x;
            else cout<<x;
    }
    const int fraction[17]={0,-50,-2,-3,-4,-5,-6,-7,-8,-9,-10,-20,-30,-40,-100,+100,0};
    int size[5];
    bool card[5][17];
    int main(){
        while(1){
            memset(card,false,sizeof(card));
            for(int i=1;i<=4;i++){
                read(size[i]);
                for(int j=1;j<=size[i];j++){
                    char k;int p;
                    cin>>k>>p;
                    if(k=='H')card[i][p]=true;
                    else if(k=='S')card[i][14]=true;
                        else if(k=='D')card[i][15]=true;
                            else if(k=='C')card[i][16]=true;
                }
            }
            if(size[1]+size[2]+size[3]+size[4]==0)return 0;
            for(int i=1;i<=4;i++){
                bool flag=true;
                for(int j=1;j<=13;j++)
                    if(card[i][j]==false){
                        flag=false;
                        break;
                    }
                if(flag){
                    int ans=200;
                    if(card[i][14]&&card[i][15])ans=500;
                    else ans=ans+card[i][14]*fraction[14]+card[i][15]*fraction[15];
                    if(card[i][16])ans*=2;
                    work(ans);
                    putchar(32);
                }else{
                    bool flg=true;
                    for(int j=1;j<=15;j++)
                        if(card[i][j]==true){
                            flg=false;
                            break;
                        }
                    if(flg){
                        if(card[i][16])work(50);
                        else work(0);
                        putchar(32);
                    }else{
                        int ans=0;
                        for(int j=1;j<=15;j++)
                            if(card[i][j])ans+=fraction[j];
                        if(card[i][16])ans*=2;
                        work(ans);
                        putchar(32);
                    }
                }
            }puts("");
        }
        return 0;
    }
    

    5. 变色龙

    错点

    (bfs)(priority)_(queue) 优化写假了。

    分析

    最短路问题。

    为什么能使用 (dij) 跑最短路?明显会 (T) 啊!

    点数:(2000 imes 2000=400,0000)

    边数:小学奥数之等差数列

    (egin{matrix}underbrace{0+4+cdots+7996}\2000项end{matrix}=(7996+0) imes 2000 div 2=7996 imes 1000=799,6000)

    我们知道 (dij) 的复杂度是 (O(n log m))

    简单计算一下

    (log m=log 7996000 approx 26)(26 imes 4000000=1,0400,0000 > 1,0000,0000‬)

    所以显然是会 (T) 掉的(除非玄学卡常,比如:等式展开之类的)

    (bfs) 的期望复杂度是 (O(n+m)) 的。

    (bfs) 大概就是这种写法

    q.push((node){xx,yy,0});
        while(q.size()){
        node f=q.top();
        q.pop();
        for(int i=0;i<4;i++){
            int x=f.x+dx[i],y=f.y+dy[i];
            if(x>=1&&x<=n&&y>=1&&y<=m){
                int as=ans[f.x][f.y];
                if(a[x][y]!=a[f.x][f.y])as++;
                if(as<ans[x][y])ans[x][y]=as,q.push((node){x,y});
            }
        }
    }
    

    总代码

    #include <bits/stdc++.h>
    using namespace std;
    template<typename T>inline void read(T &FF){
        T RR=1;FF=0;char CH=getchar();
        for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
        for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
        FF*=RR;
    }
    template<typename T>void write(T x){
        if(x<0)putchar('-'),x*=-1;
        if(x>9)write(x/10);
        putchar(x%10+48);
    }
    struct node{
        int x,y;
    };
    const int MAXN=2000+10;
    int n,m,xx,yy,a[MAXN][MAXN],ans[MAXN][MAXN];
    int dx[4]={-1,1,0,0};
    int dy[4]={0,0,-1,1};
    queue<node>q;
    int main(){
        read(n);read(m);read(xx);read(yy);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                read(a[i][j]);
        q.push((node){xx,yy,0});
        while(q.size()){
            node f=q.top();
            q.pop();
            for(int i=0;i<4;i++){
                int x=f.x+dx[i],y=f.y+dy[i];
                if(x>=1&&x<=n&&y>=1&&y<=m){
                    int as=ans[f.x][f.y];
                    if(a[x][y]!=a[f.x][f.y])as++;
                    if(as<ans[x][y])ans[x][y]=as,q.push((node){x,y});
                }
            }
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++)
                cout<<ans[i][j]<<" ";
            cout<<endl;
        }
        return 0;
    }
    

    6. 和谐分组

    讲解二分

    终于是正常一点的题目了,并且是我最喜欢的算法(蒟蒻最喜欢的算法是并查集、二分、dp、LCA)

    二分是确定一个答案然后对其分析,而答案常常有这样一种情况:

    左闭右开

    ​ 左闭右开

    右闭左开

    ​ 右闭左开

    题目通常会让我们找符合条件的最大值或最小值。

    以这道题为例,就是去在可能的谐度度中找一个最小的。

    比答案大的都可以,不答案小的都不可以。

    这个我们叫右闭左开。

    而比答案小的都可以,不答案大的都不可以。

    这个我们叫左闭右开。

    二分顾名思义,就是二分。

    左闭右开:

    int l=0,r=INT_MAX/2;
    while(l+1<r){
        int mid=(l+r)>>1;
        if(check(mid))l=mid;//这里不同
        else r=mid;//这里不同
    }
    

    右闭左开:

    int l=0,r=INT_MAX/2;
    while(l+1<r){
        int mid=(l+r)>>1;
        if(check(mid))r=mid;//这里不同
        else l=mid;//这里不同
    }
    

    分析

    现在我们已经会了二分,我们可以愉快地做这道题了。

    我们可以开始写 (check) 函数:

    bool check(int x){//check函数顾名思义,就是用来反对我们的答案x是否可行
        int maxn=a[1],minn=a[1],s=1;//初始化
        for(int i=2;i<=n;i++){
            maxn=max(maxn,a[i]);//最大
            minn=min(minn,a[i]);//最小
            if(maxn-minn>x){
                s++;//新分一个组
                maxn=a[i];//初始化
                minn=a[i];//初始化
            }
        }
        return s<=k;//如果组数小于等于k,就说明不可行
    }
    

    总代码

    #include <bits/stdc++.h>
    using namespace std;
    template<typename T>inline void read(T &FF){
        T RR=1;FF=0;char CH=getchar();
        for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
        for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
        FF*=RR;
    }
    template<typename T>void write(T x){
        if(x<0)putchar('-'),x*=-1;
        if(x>9)write(x/10);
        putchar(x%10+48);
    }
    const int MAXN=1e5+10;
    int n,k,a[MAXN];
    bool check(int x){
        int maxn=a[1],minn=a[1],s=1;
        for(int i=2;i<=n;i++){
            maxn=max(maxn,a[i]);
            minn=min(minn,a[i]);
            if(maxn-minn>x){
                s++;
                maxn=a[i];
                minn=a[i];
            }
        }
        return s<=k;
    }
    int main(){
        read(n);read(k);
        for(int i=1;i<=n;i++)read(a[i]);
        int l=0,r=INT_MAX/2;
        while(l+1<r){
            int mid=(l+r)>>1;
            if(check(mid))r=mid;
            else l=mid;
        }write(r);
        return 0;
    }
    
  • 相关阅读:
    HDU 1874 畅通工程续 (Dijkstra , Floyd , SPFA, Bellman_Ford 四种算法)
    HDU Wooden Sticks
    HDU 3664 Permutation Counting
    javascript DOM添加元素,使用节点属性
    SQL server查询数据类型为ntext是空或NULL值
    TSQL Pivot Tables(行列转换) in SQL Server 2005/2008
    ASP.NET 3.5(c#)区域化设置(LCID)
    Sql 脚本导入EXCEL数据
    asp.net 3.5 csharp 实现事务
    asp.net3.5 csharp: How to show HTML content in calendar tooltip?
  • 原文地址:https://www.cnblogs.com/zhaohaikun/p/12427657.html
Copyright © 2020-2023  润新知