zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    【Java EE 学习 36】【struts2】【struts2系统验证】【struts2 ognl值栈】【struts2 ongl标签】【struts2 UI标签】【struts2模型驱动和令牌机制】
    【Java EE 学习 35 下】【struts2】【struts2文件上传】【struts2自定义拦截器】【struts2手动验证】
    【Java EE 学习 35 上】【strus2】【类型转换器】【struts2和Servlet API解耦】【国际化问题】【资源文件乱码问题已经解决】
    【Java EE 学习 34】【struts2学习第一天】
    【JavaScript中的正则表达式】
    【Java EE 学习 33 下】【validate表单验证插件】
    【Java EE 学习 33 上】【JQuery样式操作】【JQuery中的Ajax操作】【JQuery中的XML操作】
    【Java EE 学习 32 下】【JQuery】【JQuey中的DOM操作】
    【Java EE 学习 32 上】【JQuery】【选择器】
    【Java EE 学习 31】【JavaScript基础增强】【Ajax基础】【Json基础】
  • 原文地址:https://www.cnblogs.com/wizarderror/p/12287242.html
Copyright © 2011-2022 走看看