• 《校赛题解》


    预估难度:

    签到题:学长的温馨提示,maomeng的mimi和miaomiao,maomeng简易版消消看。

    简单题:A君的音乐游戏,集训队预备队点名,学不会了,levil的三角形问题,1 ~ n的二进制表示中1的个数

    较难题:A-B Problem,levil的叠数游戏,levil的外挂,levil养鱼。

    难题:学长的口袋妖怪。

    学长的温馨提示:if依次判断每个条件是否满足。

    #include<bits/stdc++.h>
    using namespace std;
    int main() {
        long long n;cin >> n;
        if(n <= 20) printf("O(2^n)\n");
        if(n <= 100) printf("O(n^3)\n");
        if(n <= 1000) printf("O(n^2)\n");
        if(n <= 10000) printf("O(nlogn)\n");
        if(n <= 1000000) printf("O(n)\n");
        printf("O(logn)\n");
    }
    View Code

    maomeng的mimi和miaomiao:

     枚举一个人走了步数,然后计算另外一个人走几步能到最小距离。

    我们假设枚举的那个人走的步数剩下的距离 = dis。

    然后另外一个人一步能走的距离 = y。

    那么他们能走的最近的距离就为 dis % y,当dis % y != 0时。

    如果dis % y == 0,那么两个人会重合,那么第二个人就要少走一步,这个时候最小距离 = y。

    最后取最小值即可。 

    #include<bits/stdc++.h>
    using namespace std;
    
    int main() {
        int n,x,y;scanf("%d %d %d",&n,&x,&y);
        int ans = 105;
        for(int i = 0;i <= n / x;++i) {
            int dis = n - i * x;
            if(dis % y == 0) ans = min(ans,y);
            else ans = min(ans,dis % y);
        }
        printf("%d\n",ans);
    }
    View Code

    maomeng简易版消消看:

     暴力枚举两个相邻的点交换,然后判断当前是否存在一行或者一列都相同即可。

    #include<bits/stdc++.h>
    using namespace std;
    
    int a[5][5];
    bool check() {
        for(int i = 1;i <= 3;++i) {
            if(a[i][1] == a[i][2] && a[i][2] == a[i][3]) return true;
            if(a[1][i] == a[2][i] && a[2][i] == a[3][i]) return true;
        }
        return false;
    }
    int main() {
        for(int i = 1;i <= 3;++i) {
            for(int j = 1;j <= 3;++j) {
                scanf("%d",&a[i][j]);
            }
        }
        int f = 0;
        for(int i = 1;i <= 3;++i) {
            for(int j = 1;j <= 3;++j) {
                if(j != 3) {
                    int t = a[i][j];
                    a[i][j] = a[i][j + 1];
                    a[i][j + 1] = t;
                    if(check()) f = 1;
                    t = a[i][j];
                    a[i][j] = a[i][j + 1];
                    a[i][j + 1] = t;
                }
                if(i != 3) {
                    int t = a[i][j];
                    a[i][j] = a[i + 1][j];
                    a[i + 1][j] = t;
                    if(check()) f = 1;
                    t = a[i][j];
                    a[i][j] = a[i + 1][j];
                    a[i + 1][j] = t;
                }
            }
        }
        if(f) printf("Yes\n");
        else printf("No\n");
    }
    View Code

    集训队预备队点名:给后面输入的k个人名字后面拼接上21,这样就可以直接字符串比较了。

    #include <bits/stdc++.h>
    using namespace std;
    
    char a[15][15][30],s[35][30];
    bool vis[35];
    int main() {
        int n,m;scanf("%d %d",&n,&m);
        for(int i = 0;i < n;++i) {
            for(int j = 0;j < m;++j) scanf("%s",a[i][j]);
        }
        int k;scanf("%d",&k);
        for(int i = 0;i < k;++i) {
            scanf("%s",s[i]);
            int len = strlen(s[i]);
            s[i][len] = '2',s[i][len + 1] = '1',s[i][len + 2] = '\0';
        }
        for(int i = 0;i < n;++i) {
            for(int j = 0;j < m;++j) {
                for(int st = 0;st < k;++st) {
                    if(strcmp(s[st],a[i][j]) == 0) vis[st] = 1;
                }
            }
        }
        int flag = 0;
        for(int i = 0;i < k;++i) {
            if(vis[i] == 0) {
                flag = 1;
                int len = strlen(s[i]);
                for(int j = 0;j < len - 2;++j) printf("%c",s[i][j]);
                printf("\n");
            }
        }
        if(flag == 0) printf("Study hard!\n");
        return 0;
    }
    View Code

    学不会了:

    先暴力枚举第几列,然后一行行枚举计算每个字符出现的次数即可。

    #include<bits/stdc++.h>
    using namespace std;
    
    int n,m,k,cnt[30];
    char s[5005][5005];
    bool vis[5005];
    int main() {
        scanf("%d %d %d",&n,&m,&k);
        for(int i = 0;i < n;++i) scanf("%s",s[i]);
        for(int j = 0;j < m;++j) {
            memset(cnt,0,sizeof(cnt));
            for(int i = 0;i < n;++i) {
                    int x = s[i][j] - 'a';
                    cnt[x]++;
                    if(cnt[x] >= k) vis[j] = 1;
            }
        }
        for(int j = 0;j < m;++j) {
            if(vis[j]) printf("Yes\n");
            else printf("No\n");
        }
    }
    View Code

    levil的三角形问题:

    n ^ 3暴力枚举三个点,再判断是否能形成三角形。

    判断方法1:矢量叉积判断:

    #include<bits/stdc++.h>
    using namespace std;
    
    int x[305],y[305];
    bool check(int i,int j,int k) {
        if (( y[i] - y[j])*(x[i] - x[k]) == (y[i] - y[k])*(x[i] - x[j]) ) return false;
        else return true;
    }
    int main() {
        int n;cin >> n;
        for(int i = 1;i <= n;++i) cin >> x[i] >> y[i];
        int ans = 0;
        for(int i = 1;i <= n;++i) {
            for(int j = i + 1;j <= n;++j) {
                for(int k = j + 1;k <= n;++k) {
                    if(check(i,j,k)) ans++;
                }
            }
        }
        printf("%d\n",ans);
        return 0;
    }
    View Code

    判断方法2:斜率判断:

    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    int n;
    int x[330],y[330];
    int check(ll x1,ll y1,ll x2,ll y2){
        if(x1==0&&x2==0)return 0;
        else if(x1==0)return 1;
        else if(x2==0)return 1;
        return fabs(1.0*y1/x1-1.0*y2/x2)>=0.00001;
    }
    int main(){
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d%d",&x[i],&y[i]);
        }
        long long res=0;
        for(int i=1;i<=n;i++){
            for(int j=i+1;j<=n;j++){
                for(int k=j+1;k<=n;k++){
                    if(check(x[i]-x[j],y[i]-y[j],x[i]-x[k],y[i]-y[k]))res++;
                }
            }
        }
        cout<<res<<endl;
        return 0;
    }
    View Code

    1 ~ n的二进制表示中1的个数:

    做法1:找规律。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    void solve(LL n)
    {
        n++;
        LL p=1,ans=0;
        while(p<=n)
        {
            p<<=1;
            LL num=n/p,re=n%p;
            ans+=num*p/2;
            ans+=max((LL)0,re-p/2);
        }
        printf("%lld\n",ans);
    }
    int main()
    {
        LL t;
        while(~scanf("%lld",&t))
        {
            solve(t);
        }
    }
    View Code

     做法2:dp

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    
    int a[35];
    LL dp[35],pw[35];//dp[i] - 长度为i的串能形成的数量,pw[i] - 2 ^ i
    void init() {
        dp[1] = 1,pw[0] = 1,pw[1] = 2;
        for(int i = 2;i < 35;++i) {
            dp[i] = 2 * dp[i - 1] + pw[i - 1];
            pw[i] = pw[i - 1] * 2;
        }
    }
    int main() {
        init();
        int n;
        while(~scanf("%d",&n)) {
            int len = 0;
            while(n) {
                a[++len] = n % 2;
                n /= 2;
            }
            LL ans = 0;
            int pre = 0;
            for(int i = len;i >= 1;--i) {
                if(a[i] == 0) continue;
                ans += dp[i - 1] + pw[i - 1] * pre;
                pre++;
            }
            ans += pre;//全 - 1
            printf("%lld\n",ans);
        }
        return 0;
    }
    View Code

     A-B Problem : 进制转换

    先将a,b都转化为k进制下的数,然后从低位开始依次判断是否需要借位。

    #include <bits/stdc++.h>
    using namespace std;
    typedef unsigned long long LL;
    
    int a[100],b[100];
    int main() {
        LL a1,b1;scanf("%llu %llu",&a1,&b1);
        int k;scanf("%d",&k);
        int tot1 = 0,tot2 = 0;
        while(a1) {
            a[tot1++] = a1 % k;
            a1 /= k;
        }
        while(b1) {
            b[tot2++] = b1 % k;
            b1 /= k;
        }
        int cnt = 0,pos;
        for(int i = 0;i < tot2;++i) {
            if(a[i] < b[i]) {
                for(int j = i + 1;;++j) {
                    if(a[j] != 0) {
                        a[j]--;
                        pos = j;
                        break;
                    }
                }
                cnt += pos - i;
                for(int j = i + 1;j < pos;++j) a[j] = k - 1;
            }
        }
        if(cnt == 0) printf("\"XHY is so much happy!\"");
        else printf("%d\n",cnt);
        return 0;
    }
    View Code

    A君的音乐游戏:

    考虑贪心地想,分数低的操作排在前面,分数高的操作排在后面,能让最大值更大。

    分数高的操作排在前面,分数低的操作排在后面,能让最大值更小。

    然后对于前后半部分的计算都是一个等差数列的求和。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    
    int main() {
        int n;scanf("%d",&n);
        n++;
        LL mi = 0,mx = 0;
        while(n--) {
            int g,p;scanf("%d %d",&g,&p);
            LL sum = (LL)g * 1000 + (LL)(1 + g - 1) * (g - 1) / 2;
            sum += (LL)p * 2000 + (LL)(g + g + p - 1) * p / 2 * 2;
            mx += sum;
    
            sum = (LL)p * 2000 + (LL)(1 + p - 1) * (p - 1) / 2 * 2;
            sum += (LL)g * 1000 + (LL)(p + p + g - 1) * g / 2;
            mi += sum;
        }
        printf("%lld %lld\n",mi,mx);
    
    }
    View Code

    levil的叠数游戏:简单深搜

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    
    int n,x,a[25],ans;
    void dfs(int len,LL sum,int cnt1,int cnt2) {
        if(sum == x) {
            if(cnt1 != 0 && cnt1 % 2 == 0) return ;
            if(cnt2 != 0 && cnt2 % 2 != 0) return ;
            ans++;
            return ;
        }
        if(sum > x || len > n) return ;
        if(a[len] % 2 == 0) dfs(len + 1,sum + a[len],cnt1,cnt2 + 1);
        else dfs(len + 1,sum + a[len],cnt1 + 1,cnt2);
        dfs(len + 1,sum,cnt1,cnt2);
    }
    int main() {
        scanf("%d %d",&n,&x);
        for(int i = 1;i <= n;++i) scanf("%d",&a[i]);
        dfs(1,0,0,0);
        printf("%d\n",ans);
        return 0;
    }
    View Code

    levil的外挂:两次bfs

    很显然最短距离就是levil -> 小龙的最短距离 + 小龙到showmaker的最短距离。

    两次bfs求出这两个最短距离即可。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N = 55;
    
    bool vis[N][N],vt[N][N];
    int dir[8][2] = {1,0,-1,0,0,1,0,-1,1,1,1,-1,-1,1,-1,-1},n,m;
    struct Node{int x,y,z;};
    int bfs(Node st,Node ed) {
        queue<Node> Q;
        memset(vt,0,sizeof(vt));
        vt[st.x][st.y] = 1;
        Q.push(Node{st.x,st.y,0});
        while(!Q.empty()) {
            Node q = Q.front();
            Q.pop();
            if(q.x == ed.x && q.y == ed.y) return q.z;
            for(int i = 0;i < 8;++i) {
                int px = q.x + dir[i][0],py = q.y + dir[i][1];
                if(px >= 1 && px <= n && py >= 1 && py <= n && !vt[px][py] && !vis[px][py]) {
                    vt[px][py] = 1;
                    Q.push(Node{px,py,q.z + 1});
                }
            }
        }
        return -1;
    }
    int main() {
        int ca;scanf("%d",&ca);
        while(ca--) {
            memset(vis,0,sizeof(vis));
            scanf("%d %d\n",&n,&m);
            while(m--) {
                int x,y;scanf("%d %d",&x,&y);
                vis[x][y] = 1;
            }
            int x0,y0,x1,y1,x2,y2;
            scanf("%d %d %d %d %d %d",&x0,&y0,&x1,&y1,&x2,&y2);
            int d1 = bfs(Node{x0,y0,0},Node{x1,y1,0});
            int d2 = bfs(Node{x1,y1,0},Node{x2,y2,0});
            printf("we are champion!\n");
            if(d1 == -1 || d2 == -1) printf("-1\n");
            else printf("%d\n",d1 + d2);
        }
        return 0;
    }
    View Code

    levil养鱼:很显然他们的之间的差值是呈现单调递减的,所以可以二分差值。

    当差值 >= 0时,说明可能有更优取法。

    可以预处理一下两个的前缀和。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N = 1e6;
    
    LL tmp,pre[N + 5];
    int x,y;
    LL check(int i) {
        LL val1 = x + 1LL * (1 + i) * i / 2;
        tmp = val1;
        LL val2 = y + pre[i];
        LL dis = val1 - val2;
        return dis;
    }
    int main() {
        for(int i = 1;i < N;++i) pre[i] = pre[i - 1] + 2 * i - 1;
        int ca;cin >> ca;
        while(ca--) {
            scanf("%d %d",&x,&y);
            int L = 0,r = 2e3 + 1;
            LL ans = x;
            while(L <= r) {
                int mid = (L + r) >> 1;
                LL d = check(mid);
                if(d >= 0) {
                    ans = max(ans,tmp);
                    L = mid + 1;
                }
                else r = mid - 1;
            }
            printf("%lld\n",ans);
        }
        return 0;
    }
    View Code

    因为数据造的有点弱,比赛的时候被大家暴力水过去了....

    学长的口袋妖怪:状压dp

    dp[i][j] - 表示状态集为i情况下,当前站在j点的方案数。

    预处理一下子集点和非子集点来加速。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int,int> pii;
    #define dbg(ax) cout << "now is " << ax << endl
    const int N = 55;
    
    int dir[4][2] = {-1,0,0,-1,-1,-1,-1,1},mp[20][20],tim = -1,G[20],dp[1 << 20][20];
    int vec[1 << 20][21],rvec[1 << 20][21];
    bool vis[20][20];
    bool check(int x,int y) {
        if(!vis[x][y] && x >= 0 && x <= 4 && y >= 0 && y <= 4) return true;
        else return false;
    }
    void add(int x,int y) {
        G[x] |= (1 << y);
        G[y] |= (1 << x);
    }
    void init(int up) {
        for(int i = 0;i < (1 << 20);++i) {
            for(int j = 0;j < up;++j) {
                if((i >> j) & 1) {
                    ++vec[i][0];
                    vec[i][vec[i][0]] = j;
                }
                else {
                    ++rvec[i][0];
                    rvec[i][rvec[i][0]] = j;
                }
            }
        }
    } 
    void solve() {
        for(int i = 1;i <= 5;++i) {
            int x,y;cin >> x >> y;
            vis[x - 1][y - 1] = 1;
        }
        for(int i = 0;i < 5;++i) {
            for(int j = 0;j < 5;++j) {
                if(vis[i][j]) continue;
                mp[i][j] = ++tim;
                if(i == 0 || i == 4 || j == 0 || j == 4) dp[1 << tim][tim] = 1;
                for(int k = 0;k < 4;++k) {
                    int px = i + dir[k][0],py = j + dir[k][1];
                    if(check(px,py)) add(tim,mp[px][py]);
                }
            }
        }
        init(tim + 1);
        for(int i = 1;i < (1 << 20);++i) {
            int up1 = vec[i][0],up2 = rvec[i][0];
            for(int j = 1;j <= up1;++j) {
                int v = vec[i][j];
                for(int k = 1;k <= up2;++k) {
                    int vv = rvec[i][k];
                    if((G[v] >> vv) & 1) dp[i | (1 << vv)][vv] += dp[i][v];
                }
            }
        }
        LL ans = 0;
        for(int i = 0;i < 20;++i) ans += dp[(1 << 20) - 1][i];
        printf("%lld\n",ans);
    }
    int main() {
        solve();
        return 0;
    }
    View Code

     也可以用状压加上记忆化搜索的做法过。

  • 相关阅读:
    ASP.NET Core 中间件(Middleware)详解
    .NET Core 使用RSA算法 加密/解密/签名/验证签名
    【Other】希腊诸神大全-中英文名称
    【架构】分布式追踪系统设计与实现
    【架构】SpringCloud 注册中心、负载均衡、熔断器、调用监控、API网关示例
    【SpringCloud】Netflix源码解析之Ribbon:负载均衡策略的定义和实现
    【Docker】基于docker+etcd+confd + haproxy构建高可用、自发现的web服务
    【架构】Kubernetes和Spring Cloud哪个部署微服务更好?
    【Linux】Linux中 “there are stopped jobs”问题的解决方案
    【架构】分布式系统雪崩效应处理方案
  • 原文地址:https://www.cnblogs.com/zwjzwj/p/15673901.html
Copyright © 2020-2023  润新知