zoukankan      html  css  js  c++  java
  • hut 训练赛第二场 解题报告

    题目来源 HDU 2008-10 Programming Contest

    IDOriginTitle
    Problem A HDU 2520 我是菜鸟,我怕谁
    Problem B HDU 2521 反素数
    Problem C HDU 2522 A simple problem
    Problem D HDU 2523 SORT AGAIN
    Problem E HDU 2524 矩形A + B
    Problem F HDU 2525 Clone Wars
    Problem G HDU 2526 浪漫手机
    Problem H HDU 2527 Safe Or Unsafe

    Problem A

      仔细读题, 这个不是匀变速运动,  每一秒初,速度直接改变.  化简下可以得出结果为 n*n

    参考代码: 

    View Code
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    const int mod = 1e4;
    const int N = 1e5;
    typedef long long LL;
    
    int main(){
        int T, n;
        scanf("%d", &T);
        while( T--  )
        {
            scanf("%d",&n);    
            LL res = 1LL*n*n;
            printf("%lld\n", res%mod);
        }    
            return 0;
    }

    Problem B

      仔细读题,题目虽然前面一句讲的是反素数,却非要求反素数.   只需求所给区间[a,b]因子个数最多的数,当有多个解时,输出最小的.

      我们可以预处理, 将数 x 的倍数全部+1 , 通过这样枚举 x 就可以了

    参考代码

    View Code
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #define MAX(a,b) (a)>(b)?(a):(b)
    const int N = 5010;
    bool vis[N];
    int g[N], n;
    
    void init()
    {
        memset( vis ,0 , sizeof(vis));
        for(int i = 1; i < N; i++)
            g[i] = 1;
        for(int i = 1; 2*i < N; i++   )
            g[2*i]++;
        for(int i = 3; i < N; i++)
        {
            g[i]++;
            for(int j = 2; j*i < N; j++)
                g[ j*i ]++;
        }
        int Max = 0;
        for(int i = 1; i < N; i++)
        {    
            if( Max < g[i] ) vis[i] = true;
            Max = MAX( Max, g[i]);    
        }
    }
    int main()
    {
        init();    
        int T;
        scanf("%d",&T);
        while( T-- )
        {
            int a, b;
            scanf("%d%d",&a,&b);
            int Max = 0, res;
            for(int i = a; i <= b; i++)
                if( g[i] > Max )
                {    Max = g[i]; res = i; }
            printf("%d\n", res );
        }
        return 0;
    }

    Problem C

      模拟除法运算, 并标记余数,当余数为0 或者 余数已被标记, 则分别为 整除解, 第一个循环节.    

      不过这题有点恶心的地方是,如果计算写到函数中,就TLE. 只能在 main函数中操作.  1K ms . 依旧百思不得其解

      PS: 有兴趣的还可以了解下手动开根号. 12年湖南省赛就有这么一题

    参考代码

    View Code
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #include<math.h>
    
    const int N = 1e6+10;
    
    int res[N], size;
    bool vis[N];
    inline void solve(int n){
            size = 0;
            memset( res, 0, sizeof(res));
            memset( vis, 0, sizeof(vis));
            int x = 1; vis[x] = true;
            while( x )
            {
                x *= 10;
                res[size++] = x/n;
                x %= n;
                if( vis[x] ) break;
                vis[x] = true;
            }
    }
    int main(){
        
        int T, n;
        scanf("%d",&T);
        while( T-- )
        {
            scanf("%d", &n);
            if( n < 0 )
            {    printf("-"); n = -n; }    
            
            if( n == 1 ) printf("1\n");
            else{
                size = 0; 
                memset( vis, 0, sizeof(vis));
                int x = 1; vis[x] = true;
                while( x )
                {
                    x *= 10;
                    res[size++] = x/n;
                    x %= n;
                    if( vis[x] ) break;
                    vis[x] = true;
                }
                //solve(n);
                printf( "0." );
                for(int i = 0; i < size; i++)
                printf("%d",res[i]);
                puts("");
            }
        }    
            return 0;
    }

    Problem D

      因为N = 1000 ,  两两组合后极端情况有 10^6 个值, 排序时间复杂度 Nlog(N) 其中 N = n*n  , 又 C组测试数据, 总时间复杂度为 C*n^2*log( n^n) 

      当 C >= 1000 时, 时间复杂度接近 10^9 , 1,2秒内可能搞不定.

      仔细观察,  0 <= a[i] <= 2000 ,  那么 0 <= a[i] - a[j] <= 2000  , 当a[ i] >= a[j]时

      不同的数值只可能出现 2001 个.  所以我们可以使用 一个 2000的辅助数组 b[2002] 用来记录 | a[i] - a[j] | 的不同结果数量

      然后对数组 b[] 从小到大统计到第 k 个就是我们需要的结果了

      将 数组 a[] 排序后 可以避免绝对值计算

    参考代码:

    View Code
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<iostream>
    #include<math.h>
    #include<algorithm>
    using namespace std;
    const int N = 1010;
    
    int a[N],vis[4200],cnt, n, K;
    
    int main()
    {
        int T;
        scanf("%d",&T);
        while( T-- )
        {
            scanf("%d%d",&n,&K);
            memset( vis, 0, sizeof(vis));    
            cnt = 0;    
            for(int i = 0; i < n; i++)
                scanf("%d",&a[i]);
            sort( a, a+n );
            for(int i = 0; i < n; i++)
                for(int j = 0; j < i; j++)
                    vis[ a[i]-a[j] ] = 1;
            int pos = 0;
            while(K) K -= vis[pos++];
            printf("%d\n", pos-1 );
        }
        return 0;
    }

    Problem E

      递推题.  我们定义 F[n][m] 为n行m列的矩形数量

      因为 n行m列的矩形 是由 n-1行m列 通过增加一行 ,  或者 n行m-1列 通过增加一列 得到.

      我们现在只考虑 其由 n-1行m列 通过 增加一行 得到.

      通过分解. m列, 每一列都增加了一个 1 单位的小矩形.

        只对当前一列考虑时 , 其增加了 n 个 ( 考虑 n+1行1列的矩阵, 增加一行则增加了 n个矩形 ),  总共 m 列,则增加了 m*n个矩阵

        再考虑两列相邻组合情况,总共有 m-1 个 两两相邻矩阵, 同样是 n个.   总共m-1,则增加了 (m-1)*n

        再 3列, 4列 ... m列.  分别是 (m-2)*n . (m-3)*n  ... n

        累加后, m*n + (m-1)*n + .. + 1*n  

        提取 公因子n出来后, 可得 n( 1+2+...+m )  

        然后等差数列求和的到. n*m*(1+m)/2

      所以 F[n][m] = F[n-1][m] + n*m*(1+m)/2   

      预处理 F[1][i] , F[i][1] 的情况然后 预处理就可以了. n, m才100

    参考代码

    View Code
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    
    typedef long long LL;
    
    LL f[110][110];
    
    void init()
    {
        memset( f, 0, sizeof(f));
        for(int i = 1; i <= 100; i++)
            f[1][i] = f[i][1] = (1+i)*i/2;
        for(int i = 2; i <= 100; i++)
            for(int j = 2; j <= 100; j++)
                f[i][j] = f[i-1][j] + 1LL*i*j*(j+1)/2;    
    }
    int main()
    {
        init();
        int T;
        scanf("%d",&T);
        while( T-- )
        {
            int n, m;
            scanf("%d%d",&n,&m);
            printf("%lld\n", f[n][m]);
        
        }    
        return 0;
    }

    Problem F

      每一天有士兵死亡,有士兵提供材料进行克隆,有士兵克隆成功.

      注意: 士兵生存天数为[1,d] ,  可提供克隆材料为生存天数 [1,a] , 培育天数为 [0,k] , 第 k 天培育成功后, k+1 天可以执行任务和提供材料.

      这里士兵生存天数和提供克隆材料都是1开始, 是因为对于克隆培育来说, 当第 k 天培育成功后, 相对于 士兵生存天数与提供克隆材料为 第0天.  

      当k+1时,已经是第1天了,  所以我们需要区分开 时间点的相关关系问题. 

    解题代码:

    View Code
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    
    typedef long long LL;
    
    LL D[110], K[110], A[110], ans;
    int n, d, a, k, x;
    
    int main()
    {
        int T;
        scanf("%d", &T);
        while( T-- )
        {
            // 初始n个士兵
            // 每个士兵可存活 d 天
            // 培育士兵 k 天成功, k+1 天开始执行任务
            // 成功克隆后,前 a 天可提取材料
            scanf("%d%d%d%d%d",&n,&d,&a,&k,&x);
            
            memset( D, 0, sizeof(D) );
            memset(    K, 0, sizeof(K) );
            memset( A, 0, sizeof(A) );
            //初始化第0天的状态
            D[1] = 0; //目前存活了0天的士兵数量
            A[1] = 0; //可提取材料的士兵
            K[k] = n; //第0天培育成功n个士兵,并在下一天执行任务    
            ans = 0;    
            for(int day = 1; day <= x; day++)
            {
                
                LL s1 = 0, s2  = 0;    
            
                for(int i = k; i >= 0; i-- )
                    K[i+1] = K[i];
                
                for(int i = d; i >= 1; i-- )
                    D[i+1] = D[i];
                for(int i = a; i >= 1; i-- )
                    A[i+1] = A[i];
                //培养士兵k+1天开始执行任务并提供材料    
                D[1] = A[1] = K[k+1];
                for(int i = 1; i <= d; i++)
                    s1 += D[i];
                ans += s1*5;
    
                //提取材料
                for(int i = 1; i <= a; i++)
                    s2 += A[i];
                K[0] = s2;
            }
            printf("%lld\n", ans );    
        }
        return 0;
    }


    Problem G

      貌似这套题出题人有点缺心眼..

      8种模式对比得出下一行的情况.按行匹配就好了.

      对于边界我们可以将 0和 m+1 列置为 0 ,这样就简化判断了.

    参考代码

    View Code
    #include<string.h>
    #include<stdlib.h>
    #include<algorithm>
    #include<stdio.h>
    using namespace std;
    
    int dir[8][4];
    int mp[1010][1010];
    int n, m;
    
    char str[1010], tmp[10];
    
    int GetKey(int x,int y,int z){
        int a[3] = {x,y,z};
        for(int i = 0; i < 8; i++)
        {
            bool flag = true;
            for(int j = 0; j < 3 && flag; j++)
            {
                if( dir[i][j] != a[j] ) flag = false;    
            }
            if( flag ) return i;    
        }
    }
    bool legal( int x, int y)
    {
        if( (x>=1)&&(x<=n) && (y>=1)&&(y<=m) )
                return true;
        return false;
    }
    int main()
    {
        int T;
        scanf("%d", &T);
        while( T-- )
        {
            scanf("%d", &n);
            for(int i = 0; i < 8; i++)
            {
                scanf("%s %d", tmp, &dir[i][3]);
                for(int j= 0; j < 3; j++)
                    dir[i][j] = tmp[j]-'0';
            }
            scanf("%s", str);
            m = strlen( str );
            memset( mp, 0, sizeof(mp));    
            for(int i = 1; i <= m; i++)
                mp[1][i] = str[i-1]-'0'; 
            for(int i = 1; i < n; i++)
                for(int j = 1; j <= m; j++)
                {
                        int k;
                        k = GetKey( mp[i][j-1], mp[i][j], mp[i][j+1] );
                        mp[i+1][j] = dir[k][3];                    
                }
            for(int i = 1; i <= n; i++)
            {
                for(int j = 1; j <= m; j++)
                    printf("%d",mp[i][j]);
                puts("");
            }
        }
        return 0;
    }

    Problem H

      哈夫曼编码 :   我们可以这样理解, 对于一个容器, 每一个节点有一个值 (权值), 点与点之间无连接.

    每次从容器中取出两个权值最小的顶点 相连 , 并将两个顶点权值和相加后合并成一个顶点放回容器. 如此反复.得出只有一个顶点的情况, 

    整个过程中 所有合并的和相加,就是 整颗树的权值了.

      本题中, 顶点为字母,其出现次数为其权值. 按照上述方法求出后与给定值对比就可以了.

      如果使用优先队列,则要注意一个字符的情况的处理

    参考代码:

    View Code
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #include<algorithm>
    #include<queue>
    using namespace std;
    
    
    priority_queue< int, vector<int>, greater<int> > Q;
    
    char str[101010];
    int cnt[30];
    
    int main()
    {
        int T, n;
        scanf("%d",&T);
        while( T-- )
        {
            scanf("%d", &n);
            scanf("%s", str);
            memset( cnt, 0, sizeof(cnt));    
            for(int i = 0; str[i]; i++)
                cnt[ str[i]-'a' ]++;
            while( !Q.empty() ) Q.pop();
    
            for(int i = 0; i < 26; i++)
                if( cnt[i] ) Q.push(cnt[i]);
            int key = 0;    
            if( Q.size() == 1 )
            {
                key = Q.top();
                puts( key <= n ? "yes" : "no");
                continue;    
            }
            while( Q.size() > 1 )
            {
                int a = Q.top(); Q.pop();
                int b = Q.top(); Q.pop();
    //            printf("a = %d, b = %d\n", a, b);    
                key += (a+b);    
                Q.push( a+b );
            }
        
            puts( key <= n ? "yes" : "no" );
    
        }
        return 0;
    }

     

  • 相关阅读:
    令人惊艳的算法——蒙特卡洛采样法
    肺炎确诊人数增长趋势拟合和预测(截止1月28日)
    「07」回归的诱惑:深入浅出逻辑回归
    实现一个简易版Webpack
    nrm——快速切换npm源
    7个有用的JavaScript技巧
    JSON Web Token 是什么?
    【译】强化表单的9个Vue输入库
    【译】如何使用Vue过渡效果来提升用户体验
    【译】JavaScript Source Maps浅析
  • 原文地址:https://www.cnblogs.com/yefeng1627/p/2839331.html
Copyright © 2011-2022 走看看