• 2020牛客寒假算法基础集训营3


    Contest Info


    Practice Link

    Editorial

    SolvedABCDEFGHIJ
    9/10 O Ø Ø O  -  O Ø O Ø Ø
    • O 在比赛中通过 
    • Ø 赛后通过
    • ! 尝试了但是失败了
    • - 没有尝试

    Solutions


    B.牛牛的DRB迷宫II

    题意:

    在限制条件下构造一个$n*m$的迷宫使得从左上角$(1, 1)$走到右下角$(n, m)$的路径数目刚好为$k$

    思路:

    我们可以用拆位的思想,将$k$的二进制按位进行拆解。比如$19$我就可以写作$10011=2^{4}+2^{1}+2^{1}$,我们只要能得到$2^{x}$的构造,其他的数当然也能被构造出来。

    接着可以构造一个二进制编码器,斜对角线上的方案数恰好是$1,2,3,4,8,16,32...$,只要这个数二进制某位上是$1$,我们就在其对应的二进制方案数上开一条向下的通道就可以了(R->B, 向下依次都变成U,最后一行都是R向右求和,毕竟是个二进制编码器嘛)

    C.牛牛的数组越位

    模拟,给不同的情况打上标记。比赛时候$WA$是因为只用了一个标记记录两种情况,然后那个标记就在两种情况下轮流赋值就出现错误

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    #define ll long long
    using namespace std;
    const int maxn = 2e7+100;
    ll T, n, m, p, x, y, c;
    ll a[maxn];
    int main(){
        scanf("%lld", &T);
        while(T--){
            scanf("%lld%lld%lld", &n, &m, &p);
            ll lim = n*m-1, fg = 0;
            for(int i = 0; i <= lim; i++) a[i] = 0;
            while(p--){
                scanf("%lld%lld%lld", &x, &y, &c);
                if(x<0||x>n-1||y<0||y>m-1) fg = 1;
                ll tmp = m*x+y;
                if(tmp>=0&&tmp<=lim) a[tmp] = c;
                else fg = 2;
            }
            if(fg==2) printf("Runtime error
    ");
            else if(fg==1){
                for(int i = 0; i <= lim; i++){
                    if((i+1)%m==0) printf("%d
    ", a[i]);
                    else printf("%d ", a[i]);
                }
                printf("Undefined Behaviour
    ");
            }
            else{
                for(int i = 0; i <= lim; i++){
                    if((i+1)%m==0) printf("%d
    ", a[i]);
                    else printf(" %d", a[i]);
                }
                printf("Accepted
    ");
            }
        }
    }
    Wrong Answear
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    #define ll long long 
    using namespace std;
    const int maxn = 2e7+100;
    int T, n, m, p, x, y, c;
    int a[maxn];
    int main(){
        scanf("%d", &T);
        while(T--){
            scanf("%d%d%d", &n, &m, &p);
            ll lim = n*m-1, fg1 = 0, fg2 = 0;
            for(int i = 0; i <= lim; i++) a[i] = 0;
            while(p--){
                scanf("%d%d%d", &x, &y, &c);
                if(x<0||x>n-1||y<0||y>m-1) fg1 = 1;
                ll tmp = m*x+y;
                if(tmp>=0&&tmp<=lim) a[tmp] = c;
                else fg2 = 1;
            }
            if(fg2) printf("Runtime error
    ");
            else{
                for(int i = 0; i <= lim; i++){
                    if((i+1)%m==0) printf("%d
    ", a[i]);
                    else printf("%d ", a[i]);
                }
                if(fg1) printf("Undefined Behaviour
    ");
                else printf("Accepted
    ");
            }    
        }
    }
    Accepted

    D.牛牛与二叉树的数组存储

    同C题模拟

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define ll long long
    using namespace std;
    const int maxn = 1e5+100;
    int n, cnt;
    int a[maxn<<1], id[maxn<<1];
    int main(){
        scanf("%d", &n);
        for(int i = 1; i <= n; i++){
            scanf("%d", &a[i]);
            if(a[i]==-1) continue;
            id[a[i]] = i, cnt++;
        }
        a[0] = -1;
        for(int i = n+1; i <= 2*n+1; i++) a[i] = -1;
        printf("The size of the tree is %d
    ", cnt);
        printf("Node %d is the root node of the tree
    ", a[1]);
        for(int i = 1; i <= cnt; i++){
            int rt = id[i];
            printf("The father of node %d is %d, the left child is %d, and the right child is %d
    ", i, a[rt>>1], a[rt<<1], a[rt<<1|1]);
        }
    }
    View Code

    J.牛牛的宝可梦Go

    题意:

    给定权值全为1的$m$条道路连通$n$个城市,在特定时间$t$,地点$p$会出现价值为$val$的宝可梦,且保证同一时间点不会出现两只宝可梦。起始位置为1号城市,起始时间为0时刻,问他能捕获的宝可梦价值之和最大为多少

    思路:

    既然需要在不同城市间移动,想尽可能多的捕获宝可梦,那么就需要走最短路,首先跑一遍$Floyd$,得出各点对间的最短路

    接下来就需要选择我们捕获的宝可梦,那我们去捕捉哪些宝可梦才能使得价值之和最大了呢?

    我们定义$dp[i]$为最后捕获的宝可梦为$i$的最大价值,那当前状态可以由此时刻前最后捕获的宝可梦为$j$的最大价值转移得到

              $dp[i] = dp[j] + val[i]$

    当然前提是我能在$i$出现前赶到他所在的地点,即$dis[i][j]<=t[i]-t[j]$

    为了更方便的转移,我们可以把这些宝可梦按出现的时间进行排序,这样就可以快速遍历此时刻前捕获的宝可梦们了

    但是你这个时候会发现一个问题,我照这样去转移的话,时间复杂度为$O(n^{2})$,肯定会超时。实际上从最近200个开始转移就可以了,因为200个以外的,都距离超过200个时间了(只有200个城市),必能走到。这样就把复杂度从$O(n^{2})$降到了$O(200*n)$

    由此可以得到,$0到i-200$这些都能直接转移,我们只需要记录最大值$maxval$, 最后将$maxval+val[i]$和$i-200$到$i$转移后的最大值进行比较就可以了。

    注意!!!$dp$数组初始化为$-inf$。因为题目要求人一开始在1号点,所以一开始可能有点赶不过去, $-inf$保证了起点是1号点

    比如,排序后有宝可梦1、2、3、4,这时1、2都不能到达,假如3此时从2转移,如果$d[2]$为0的话,那么$dp[3]$就会更新为$val$, 但是明明就不能从1到2,根本就不存在1->2->3这条路径

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define ll long long
    using namespace std;
    const int maxn = 210, maxm = 1e5+100;
    int n, m, k, x, y, dis[maxn][maxn];
    ll dp[maxm], ans;
    struct node{
        int t, p, val;
    }a[maxm];
    bool cmp(node a, node b){
        return a.t < b.t;
    }
    int main(){
        scanf("%d%d", &n, &m);
        memset(dis, 0x3f, sizeof(dis));
        for(int i = 1; i <= n; i++) dis[i][i] = 0;
        while(m--){
            scanf("%d%d", &x, &y);
            dis[x][y] = dis[y][x] = 1;
        }
        for(int k = 1; k <= n; k++)
            for(int i = 1; i <= n; i++)
                for(int j = 1; j <= n; j++)
                    dis[i][j] = min(dis[i][j], dis[i][k]+dis[k][j]);
        
        scanf("%d", &k);
        for(int i = 1; i <= k; i++)scanf("%d%d%d", &a[i].t, &a[i].p, &a[i].val);
        sort(a+1, a+1+k, cmp);
        a[0].p = 1; //初始位置为1, 时刻为0 
        for(int i = 1; i <= k; i++) dp[i] = -1ll<<33;
        for(int i = 1; i <= k; i++){
            for(int j = i-1; j >= max(0, i-200); j--)
                if(dis[a[i].p][a[j].p]<=a[i].t-a[j].t)
                    dp[i] = max(dp[i], dp[j]+a[i].val);
            ans = max(ans, dp[i]); //这里的ans就相当于maxval 
        }
        printf("%lld", ans); 
        return 0;
    }
    View Code

    (这题的价值应该在于$dp$的应用,作者提到和求最长上升子序列问题的过程类似,至于最短路只是求个路径而已,反正历次比赛我$dp$的定义和转移就基本想不到)

            

     


    References:

     https://www.cnblogs.com/FYH-SSGSS/p/12285373.html

  • 相关阅读:
    数据库
    Python基础
    flask 文件的上传下载和excel操作
    Django 知识点补充
    Django部署时为什么要用 uWSGI与 Nginx? 以及 WSGI,uwsgi等协议
    Memcached 补充
    websocket基本概念
    Tornado 异步非阻塞
    Tornado 自定义session,与一致性哈希 ,基于redis 构建分布式 session框架
    functools 和 itertools
  • 原文地址:https://www.cnblogs.com/wizarderror/p/12287242.html
Copyright © 2020-2023  润新知