zoukankan      html  css  js  c++  java
  • 湖南工业大学个人选拔赛第一场 解题报告

    Problem A 客户数量

      可以得出将长度为 len的蛋糕切成 len段 1的,不管如何切,总花费都为 len*(len-1)/2

      对于每个顾客,来到的时间st,以及需要的蛋糕长度k,则有一个终止时间ed = st+k*(k-1)/2

      对每个顾客求出其服务终止时间,然后得到n个区间,将区间以终点排序,然后线性扫过去,用i的起点时间与前一个服务顾客

      的终点时间比较即可。

      时间复杂度 O( NlogN )

    View Code
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cassert>
    using namespace std;
    const int N = 1e5+10;
    typedef long long LL;
    const int Max = (int)(1e9);
    struct node{ 
        LL st, ed, k;    
        bool operator < ( node tmp ) const{
            if( ed == tmp.ed ) st > tmp.st;
            return ed < tmp.ed;    
        }
        void input(){
            scanf("%lld%lld", &st, &k);    
            assert( st>=0 && st<=Max );
            assert( k>=0 && k<=Max );
            ed = st + k*(k-1)/2;
        }
    }p[N];
    bool vis[N];
    int n;
    
    int main(){ 
        freopen("2.in","r",stdin);
        freopen("2.out","w",stdout);
        while( scanf("%d",&n) != EOF ){ 
            assert( n >= 0 && n <= 100000 );
            int k, ans = 0;
            int x, t; 
            for(int i = 0; i < n; i++)
                p[i].input();
            sort( p, p+n );
            int idx = 0;
            memset( vis, 0, sizeof(vis));
            for(int i = 0; i < n; i++){ 
                if( p[i].st >= idx ){
                    if( p[i].st == p[i-1].st && p[i].ed == p[i-1].ed ){
                        continue;
                    }
                    vis[i] = 1;
                    idx = p[i].ed;
                    ans++;
                }
            }
            printf("%d\n", ans );
        }
        return 0;    
    }

    Problem B 最小整齐度

      dp(i,j) 表示前i个单词,最后一行字符数量为j的,最小整齐度。其中最后一行不计算花费

      则若已知 dp(i,j) 的情况下,添加第 i+1 个单词进来只会导致两种情况:

        1,放于最后一行,不换行,此时不增加花费。

        2,换行,此时花费增加 (M-j)^3

      则状态方程为

        情况一: dp( i, j+1+L_(i+1) ) = Min{ dp(i,j) }

        情况二: dp( i, L_(i+1) ) = Min{ dp(i,j) + (M-j)^3 }

      注意两种情况成立的 条件判定。

    View Code
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cassert>
    #include<algorithm>
    using namespace std;
    #define cmp(x) (x)*(x)*(x)
    typedef long long LL;
    const LL inf = ~0llu>>1; 
    LL dp[2][510];
    int L[2010];
    int n, m;
    void DP(){
        memset( dp, 0xff, sizeof(dp) );
    //    for(int i = 0; i < m; i++) dp[0][i] = inf;
        dp[0][0] = 0;
        for(int i = 0; i < n; i++){
            int cur = i&1, nxt = (i+1)&1;
            for(int j = 0; j < m; j++) dp[nxt][j] = -1; 
            for(int j = 0; j < m; j++){
                if( (dp[cur][j] != -1)   ){ 
                    if( j == 0 ){
                        int len = (j+L[i+1] == m )? 0 : j+L[i+1];
                        if( dp[nxt][len] == -1 ) dp[nxt][len] = dp[cur][j];
                        else dp[nxt][len] = min( dp[nxt][len], dp[cur][j] );    
                    }
                    else if( j+1+L[i+1] <= m ){
                        int len = (j+1+L[i+1] == m) ? 0 : j+1+L[i+1];
                        if( len < m ){
                            if( dp[nxt][len] == -1 ) dp[nxt][len] = dp[cur][j];
                            else dp[nxt][len] = min( dp[nxt][len], dp[cur][j] );    
                        
                        }
                    }  
                    if( j > 0 ){
                        int k = (L[i+1] == m) ? 0 : L[i+1];
                        if( dp[nxt][ k ] == -1 ) 
                            dp[nxt][ k ] = dp[cur][j] + cmp(m-j);
                        else
                            dp[nxt][ k ] = min( dp[nxt][ k ], dp[cur][j] + cmp(m-j) );
                    }
                } 
            }
            //for(int j = 0; j < m; j++){
            //    printf("%lld ", dp[nxt][j] );    
            //}     puts("");
        } 
        LL ans = -1;
        for(int i = 0; i < m; i++){
        //    printf("%lld\n", dp[n&1][i] );
            if( dp[ n&1 ][i] != -1 ){
                if( ans == -1 ) ans = dp[n&1][i];
                else ans = min( ans, dp[n&1][i] );    
            } 
        }
        if(ans == -1) ans = 0;
        printf("%lld\n", ans );
    }
    
    int main(){
        freopen("2.in","r",stdin);
        freopen("2.out","w",stdout);
        while( scanf("%d%d", &n,&m) != EOF){
            assert( n>=1 && n<=2000 );
            assert( m>=1 && m<=500 );
            for(int i = 1; i <= n; i++){
                scanf("%d", &L[i] );
                assert( L[i]>=1 && L[i]<=m );
            }
            DP();    
        } 
        return 0;    
    }

    Problem C 最小覆盖点

      我们可以令 每个区间的 [l,r] ,l 顶点为-1, r顶点为 +1, 则只有当 x < l or x >= r  时候 前缀和才会为0.

      这样将时间复杂度将为 O(N)

    View Code
    #include<stdio.h>
    #include<math.h>
    #include<string.h>
    #include<algorithm>
    #include<assert.h>
    using namespace std; 
    const int N = 2e5+10;
    int t[N],n, m,ans;
    main()
    { 
        freopen("3.in","r",stdin);
        freopen("3.out","w",stdout);
        while(scanf("%d%d",&n,&m) !=EOF )
        {
            assert( n>=1 && n<=20000 );
            assert( m>=1 && m<=50000 );
            ans=0;
            memset(t,0,sizeof(t));
            int a, b, j = 0;
            for(int i = 0; i < m; i++){
                scanf("%d%d",&a,&b);
                if( a > b ) swap(a,b);
                assert( a>=1 && a<=n );
                assert( b>=1 && b<=n );
                t[a] += 1; t[b] -= 1;
            }
            for(int i = 1; i <= n; i++ )
            { 
                j += t[i];
                if( j == 0 ) ans++;
            }
            printf("%d\n",ans);
        }
        return 0;
    }

      当然也可用用 线段树 or 树状数组 O( NlogN ) 的写法,同样能过

    Problem D 1的数量 

    组合数学, 通过计算 [1,n]区间数量解决.

        假设n十进制表示位数为L,则通过统计长度为 L, L-1, ..., 1的数量来得出结果.    

        假设 n = a1,a2,a3,...,aL    // 十进制表示,其中a1为高位

        一.当长度小于L, 假设其为 len = 1,2,...,L-1

          则当前数 X = a1,a2,a3,...,a_len

          因为 Length(X) < Length(N), 所以任意的X都小于N,

          我们可以枚举 X中包含的1的个数,假设其为 K, 则 K = 0,1,2,...,len

          这里考虑, 因为高位不能为0,而其它位可以为 0.

          这里分两种情况:

            1. a_1 为1,  则 a_2,..,a_len ,中有 k-1位取1, 有 n-k位不取1,

    对于不取1的位置,其可以取(0,2,3,..,9) ,所以方案数为 C(n-1,k-1) * 9 ^ (n-k).

            1. a_1 不为1, 则 a_2,...,a_len,中有 k个为取1, 有n-k位不取1,

    对于不取1的位置, 若是a_1位置则只能取(2,3,..,9)共8种,而a_i ( i != 1 ) 则可以取( 0,2,..,9 )共9种,所以方案数位 8*9^(n-1-k)*C(n-1,k) 

          而 k 的取值范围为 (0,1,2,..,len ) 

          则总和为

                  注意 k = len 则要单独计算因为没有 a_1不为1的情况。

        二.当长度等于L

          若当前数 X = a1,a2,a3,...,a_L

          我们则一位一位处理, 当我们处理当 第i位, 则 (1,i-1)位 为1的数量为 cnt

          若 a_i < 0 , 则当前数 X 要小于 N, 则当前位只能取0. 

          若 a_i = 1 , 则当前数 X 要小于 N, 则当前位可以取0

              则 X' = a_1,a_2,...,a_i = 0, a_(i+1)..., a_L  

              此时, 对于 a_(i+1) ,..., a_L 位(共有L-i位)而言,取任意数都 会使 X' < N , 意味着都满足要求.

              则我们可以通过枚举 此区间1的个数, 假设其为 k = 0,1,2,.., L-i 

              则方案数为   (cnt+k)*C(L-i, k)*9^(L-i-k) 

          若 a_i > 1 , 则当前数 X 要小于 N, 则当前位可以取 ( 0,1,2, ..., (a_i-1) )

              这里其实和 a_i = 1 计算方式差不多, 只是要注意.

              若我们取 a_i = 1, 则 1的位数统计的时候就要多一个 即 cnt+1+k

              对于 a_i 不取 1, 则为  cnt+k ...

    View Code
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<map>
    #include<string>
    #include<cmath>
    #include<cassert>
    using namespace std;
    const int mod = 1e9+7;
    typedef unsigned long long LL;
    const LL Max = (LL)(1000000000000000000ll);
    
    LL C[20][20], fact[20];
    void init(){
        for(int i = 0; i <= 18; i++)
            C[i][0] = C[i][i] = 1;
        for(int i = 2; i <= 18; i++ ){
            for(int j = 1; j < i; j++)
                C[i][j] = (C[i-1][j-1] + C[i-1][j])%mod;    
        }    
        fact[0] = 1;
        for(int i = 1; i <= 18; i++) fact[i] = fact[i-1]*9%mod;
    }
    LL sum( LL x ){
        LL res = 0;
        if( x <= 0 ) return res; 
        int a[20], n = 0;
        LL t = x; while( t ){ t/=10; n++; }
        t = x;
        for(int i = n; i >= 1; i-- ){
            a[i] = t%10; t /= 10;
        } 
        for(int L = 1; L <= n-1; L++ ){
            res += L;
            for(int k = 1; k < L; k++){
                res += 1LL*k*( fact[L-k]*C[L-1][k-1] + 8LL*fact[L-1-k]*C[L-1][k] )%mod;
            }
        }
    //    printf("res = %I64u\n", res ); //Accepted
        int c = 0;
        LL tmp = res;
        for(int i = 1; i <= n; i++){
            if( a[i] == 1 ){
                if( i > 1 ){
                    LL tmp = 0;
                    for(int j = 0; j <= n-i; j++){
                        tmp += (c+j)*( C[n-i][j]*fact[n-i-j] );    
                        tmp %= mod; 
                    }   
                    res = (res+tmp)%mod;
                }
                c++;    
            }
            else if( a[i] > 1 ){
                LL tmp = 0;
                for(int j = 0; j <= n-i; j++){
                    tmp += (c+1+j)*( C[n-i][j]*fact[n-i-j] );    
                    if( i == 1 ) tmp += (a[i]-2)*(c+j)*( C[n-i][j]*fact[n-i-j] )%mod;
                    else tmp += (a[i]-1)*(c+j)*( C[n-i][j]*fact[n-i-j] )%mod;
                    tmp %= mod;
                }  
            //    printf("tmp = %I64u\n", tmp );
                res += tmp; res %= mod;
            }    
        } 
    //    printf("add = %I64u\n", res+c - tmp ); // Wrong Answer  1248
        return (res + c)%mod;
    }
    int main(){
    //    freopen("3.in","r",stdin);
    //    freopen("3.out","w",stdout);
        init(); 
        LL a , b;
        while( scanf("%llu%llu", &a,&b) != EOF ){  
            if( a > b ) swap( a, b );
            assert( a >= 1 && a <= Max );
            assert( b >= 1 && b <= Max );
            printf("%llu\n", (sum(b)-sum(a-1)+mod)%mod );  
        }
        return 0;    
    }

    Problem E

        暴力枚举得到的组合情况 总共为 9! = 3*10^6, 然后对于和适度可以边枚举边计算,然后优化掉比较的时间。

        所以总时间复杂为 O( 9! ) = O( 10^6 )

        

    View Code
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<assert.h>
    #define MIN(a,b) (a)<(b)?(a):(b)
    const int inf = 0x3fffffff;
    const int Mod = 1000000;
    const int Max = (int)(1e9);
    
    int g[15][15], a[15], n, len;
    int ans;
    int b[15];
    
    bool vis[10];
    
    void dfs( int num, int sum ){
        if( sum > ans ) return;
        if( num == len ){ 
            if( sum < ans ) ans = sum;
            return;
        }    
        for(int i = 0; i < len; i++){
            if( !vis[i]  ){ 
                if( num == 0 ){
                    if( a[i] == 0 ) continue;
                    vis[i] = 1;
                    b[num] = a[i];
                    dfs( num+1, 0 );
                    vis[i] = 0;
                }
                else{ // num > 0 
                    if( sum + g[a[i]][b[num-1]] < ans ){
                        vis[i] = 1;
                        b[num] = a[i];
                        dfs( num+1, sum+g[a[i]][b[num-1]] );
                        vis[i] = 0;    
                    }
                }
            }    
        }
    } 
    
    int main(){
        freopen("4.in","r",stdin);
        freopen("4.out","w",stdout);
        int T, X;
        scanf("%d", &T);
        while( T-- ){
            scanf("%d", &X );assert( X>=1 && X<=Max );
            for(int i = 0; i < 10; i++)
                for(int j = 0; j < 10; j++ ){
                    scanf("%d", &g[i][j] ); assert(g[i][j]>=0 && g[i][j]<=Mod);
                }
            ans = inf;len = 0;
            int t = X;
            
            while( t ){
                a[len++] = t%10;
                t /= 10;    
            }
            memset( vis, 0, sizeof(vis) );
            dfs( 0, 0 );
            printf("%d\n", ans );
        }    
        return 0;    
    }
  • 相关阅读:
    [转载]DB2数据库移植罕见成绩片面理睬(4)
    [转载]接续刷新Sybase数据库后果大搜聚1
    [转载]DB2数据库移植罕见结果周全理会(3)
    [转载]有关PB/Sybase斥地过程中数据库转移引见 (2)
    [转载]如何将sybase的sa暗码重置为空
    [转载]DB2数据库移植罕有成绩片面解析(1)
    [转载]精益求精Sybase数据库题目成果年夜征求8
    [转载]精益求精Sybase数据库标题大包括6
    [转载]千锤百炼Sybase数据库题目大大搜罗7
    [转载]DB2数据库移植罕有结果片面解析(2)
  • 原文地址:https://www.cnblogs.com/yefeng1627/p/3016398.html
Copyright © 2011-2022 走看看