hut 训练赛第二场 解题报告

题目来源 HDU 2008-10 Programming Contest

 

ID Origin Title
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;

}

 

 

你可能感兴趣的:(T)