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;
}

 

 

posted @ 2012-12-29 21:59  yefeng1627  阅读(215)  评论(0编辑  收藏  举报

Launch CodeCogs Equation Editor