zoukankan      html  css  js  c++  java
  • 2013511 湘潭多省程序设计 赛后总结

    A  Alice and Bob

    解法一, 比赛时用的解法是,因为N<=10000,那么枚举A,B分别拿的时候数量,然后求最小,然后得出A,B拿的最小次数的上下界比较,得出区间的几个关系,因为k1>=k2,判定有点复杂。特殊情况比较多。

    解法二,是用动态规划,状态方程 dp( i, j ) , 表示剩下 i 块石头,j = 0时,最后一次是A拿最小次数, j = 1,最后一次是B拿最小次数。

    转移方程为  dp( i, 0 ) = min{ dp(i-2^k,1) } +1,  dp( i, 1 ) = min{ dp(i-3^k, 0) } +1 .

    比较下还是解法二比较好,没那么多特殊情况判定,写起来也比较快。

    View Code
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N = 10010;
    const int inf = 0x3f3f3f3f;
    int dp[N][2];
    int a[20], b[20];
    
    int main(){
        a[0] = b[0] = 1;
        for(int i = 1; i <= 20; i++) a[i] = a[i-1]*2, b[i] = b[i-1]*3;
        int T, n;
        scanf("%d", &T);    
        while( T-- ){
            scanf("%d", &n);
            memset(dp,0x3f,sizeof(dp));
            dp[0][0] = dp[0][1] = 0;
            for(int i = 1; i <= n; i++){
                int t = inf;    
                for(int k = 0; a[k] <= i; k++)    
                    t = min( t, dp[ i-a[k] ][1] );
                dp[i][0] = t+1;
                t = inf;
                for(int k = 0; b[k] <= i; k++)
                    t = min( t, dp[ i-b[k] ][0] );
                dp[i][1] = t+1;
            } 
            printf("%d\n", dp[n][0] );    
        }
        return 0;
    }

    B Binary Search Tree

    因为给定的节点权值都为整数,可以调整权值为浮点数.意味着两个整数间存在任意多个数. 通过中序遍历树后, 求出 LIS, 则 N-LIS是最少需要改变的.

    这里因为 N = 10^5, 需要使用 O(NlogN)的求法.  说白了就是用了个 二分查找.

    View Code
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    using namespace std;
    const int N = 5010;
    const int inf = 0x3f3f3f3f;
    struct node{
        int key, lch, rch;
    }Q[N];
    
    int a[N], pre[N], n, top;
    int idx[N], m;
    
    void gao(int x){
        if( Q[x].lch != 0 ) gao( Q[x].lch );
        a[ ++top ] = Q[x].key;
        if( Q[x].rch != 0 ) gao( Q[x].rch );
    }
    int find(int x){
        int l = 0, r = m, res = 0;
        while( l <= r ){
            int mm = (l+r)>>1;
            if( x > idx[mm] ) res=mm,l = mm+1;
            else r = mm-1;
        }
        //printf("res = %d\n", res );
        return res;
    }
    int LIS(){
        m = 0;
        for(int i = 0; i <= n; i++)
            idx[i] = -inf;
        for(int i = 1; i <= n; i++){
            int t = find( a[i] )+1;
            idx[t] = a[i];    
            if( t > m ) m = t;
        }
        return m;
    }
    int main(){
        int T;
        scanf("%d", &T);
        while( T-- ){
            scanf("%d", &n);
            memset(pre,0,sizeof(pre));    
            for(int i = 1; i <= n; i++){
                scanf("%d%d%d", &Q[i].key,&Q[i].lch,&Q[i].rch);    
                pre[ Q[i].lch ] = i; pre[ Q[i].rch ] = i;    
            }
            int rt; 
            for(int i = 1; i <= n; i++)
                if( pre[i] == 0 ){ rt = i; break; }
            top = 0;    
            gao(rt);
            //for(int i = 1; i <= n; i++) 
            //    printf("%d ", a[i] ); puts("");
            printf("%d\n", n-LIS() );    
        }
        return 0;
    }

    C  Coins

    题目大意是给N个区间形如[l,r] 其有个最小值,然后求最小和.

    解法:  离散化后转换成区间覆盖问题.然后统计.

      因为离散化的缘故,涉及端点问题, 解决此题最核心的地方在于,将 [ l, r ] 闭区间转换成 [ l, r+1 )的左闭右开 的线段. 然后线段书或者用 并查集 随便搞搞就出来了.  解题代码 与 更详细的请跳转: http://www.cnblogs.com/yefeng1627/archive/2013/05/15/3079973.html

    D DNA

    虽然知道大致 思路是, dp( i )表示 前i个字符,最大价值.  然后转移 dp( i ) = max( dp( i-j ) + cost( key(x) ) } , 算当前字符能得到哪些单词.要用AC自动机.不太熟..这两天去弄明白了再回头补上...

    E Edges of Triangle

    解题报告的解法是, 处理两个端点使其为整点, 那么就可以用 gcd( (x2-x1), (y2-y1) )  来解.  其求解原理如下:

      假定 两端点为整点, (x1,y1), (x2,y2) ,  则直线线形式如下:  x/a + y/b = 1 .

      将方程转换下,  得到  b*x + a*y = a*b , 然后根据 扩展欧几里德定理得到解的形式为 :  x = x0 + t* (a/gcd(a,b)).   t为任意整数

      我们知道改直线 经过点 (0,b), 则 x0 = 0, 是直线的一个解, 因为 满足要求的 x 取值范围为 [ 0, a ] ,则 t的取值范围即为 0, 1, ...,gcd(a,b)  所以 直线 b*x+a*y = a*b在区间[0,a]上的整点数量为 gcd( a,b )+1.  

      通过以上分析. 我们知道 为什么能够用 gcd( (x1-x2), (y1-y2) ) 来计算 直线上的点数量.  

      但是在这题,这样做却不可行.   因为 两个端点 (x1,y1),(x2,y2)并非整点. 所以就不能得到上面的 直线方程.就不满足上面计算公式. 这也是为什么要详细指出上面的计算原理.

    若要使其 能够用以上公式计算. 则必须要 找到  两端的整点 .A( x1`, y1` ), B( x2`, y2` )  , 才可以带入计算.  

      而 两端的整点 ,  若我们平行看支线四个点的关系.  有    (x1,y1) <=  A(x1`,y1`) <= B(x2`,y2`)  <= (x2,y2) ,  取等号是因为 (x1,y1),(x2,y2)可能为整点.

    求法, 是用 EXgcd求出 满足要求的 x0,  然后 其通解形式如  x0 + t*b` ,  然后找到第一个大与 (x1,y1)的整点, 找到最后一个小于 (x2,y2)的整点. 然后带入计算即可. 

      长篇大论了一通.  主要是涉及理论.  建立在 扩展欧几里德定理之上.  

      其实话说回来, 既然都已经用 exgcd 求出了 x0, 也可以直接算 出当期支线 A*x+B*y = C 在区间 [x1,x2]上整点数量即可. 不要要分情况讨论,  x0 与 区间[x1,x2]的三种情况.

    另外, 三条支线间点的计算要注意重复问题.  简单点的可以用 map< pair<LL,LL>, int >  来标记.  

    View Code
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<map>
    #include<algorithm>
    using namespace std;
    
    typedef long long LL;
    map< pair<LL,LL>,int > mp;
    const double esp = 1e-8;
    int sign(double x){
        return x<-esp?-1:(x>esp);
    }
    LL gcd(LL a, LL b){
        return b == 0 ? a : gcd(b,a%b);
    }
    LL ExGcd(LL a, LL b, LL &x, LL &y){
        if( b == 0 ){
            x = 1, y = 0;
            return a;
        }
        int r = ExGcd( b, a%b, x, y );
        LL t = x;
        x = y;
        y = t - a/b*y;
        return r;
    }
    
    int A1, B1, C1;
    int A2, B2, C2;
    int A3, B3, C3;
    
    bool find(LL x,LL y){
        if( mp.count(make_pair(x,y) ) == 0 ){
            mp[make_pair(x,y)] = 1;
            return false;
        }
        return true;
    }
    bool equal( double x, double y ){
        if( sign(x-y) == 0 ) return true;
        return false;
    }
    LL fun(double x1,double y1, double x2, double y2,int A, int B,int C){
        //printf("A = %d, B = %d, C = %d\n", A,B,C);    
        LL n = 0;    
        if( B == 0 ){ // x = C/A
            if( C%A != 0 ) return 0;
            if( sign(y1-y2) > 0 ) swap(y1,y2);
            n = (LL)( floor(y2)-floor(y1)+1 );    
        //    printf("B = 0, n = %lld\n", n );    
            if( sign( y1-floor(y1) ) == 0 ){ // if y1 is endpoint
                if( find(x1,(LL)floor(y1) ) ) n--;
            }    
            if( sign( y2-floor(y2) ) == 0 ){ // if y2 if endpoint
                if( find(x1,(LL)floor(y2) ) ) n--;    
            }    
        }
        else if( A == 0 ){ // y = C/B 
            if( C%B != 0 ) return 0;    
            if( sign(x1-x2) > 0 ) swap(x1,x2);
            n = (LL)(floor(x2)-floor(x1)+1);    
            //printf("A = 0, n = %lld\n", n );    
            if( sign( x1-floor(x1) ) == 0 ){
                if( find((LL)floor(x1),y1 ) ) n--;;
            }
            if( sign( x2-floor(x2) ) == 0 ){
                if( find( (LL)floor(x2),y1 ) ) n--;
            }    
        }
        else{
            if( sign(x1-x2) >= 0 ) swap(x1,x2),swap(y1,y2); //x1<x2    
            LL d = gcd( A, B );
            if( C%d != 0 ) n = 0;
            else{
                LL x0, y0, AA = A/d, BB = B/d;    
                ExGcd( A/d, B/d, x0, y0 );        
                x0 *= C/d, y0 *= C/d;    
                //printf("x0 = %lld, y0 = %lld\n", x0, y0 );    
                if( (sign(x0-x1)>=0) && (sign(x2-x0)>=0) ){ // x1, x0, x2
                    n = abs((LL)floor( (x0-x1)/BB )) + abs((LL)floor( (x2-x0)/BB )) + 1;
                //    printf("case 1:  n = %lld\n", n );    
                    LL k = (LL)(floor((x0-x1)/BB));    
                    if( equal(y1,floor(y1)) && equal(x1,x0-BB*k) && equal(y1,y0+AA*k) ){
                        if( find( x0-BB*k, y0+AA*k ) ) n--;    
                    }
                    k = (LL)(floor( (x2-x0)/BB ));    
                    if( equal(y2,floor(y2)) && equal(x2,x0+BB*k) && equal(y2,y0-AA*k) ){
                        if( find( x0+BB*k, y0-AA*k ) ) n--;    
                    }
                }    
                else if( sign(x1-x0) >= 0 ){ // x0, x1, x2
                    n = abs( (LL)floor((x2-x0)/BB) ) - abs( (LL)floor((x1-x0)/BB));        
                //    printf("Case 2: n = %lld\n", n );    
                    LL k = (LL)(floor((x1-x0)/BB));        
                    if( equal(y1,floor(y1)) && equal(x1,x0+BB*k) && equal(y1,y0-AA*k) ){
                        if( !find( x0+BB*k, y0-AA*k ) ) n++;    
                    }
                    k = (LL)( floor(x2-x0)/BB );
                    if( equal(y2,floor(y2) ) && equal(x2,x0+BB*k) && equal(y2,y0-AA*k) ){
                        if( find( x0+BB*k, y0-AA*k ) ) n--;    
                    }
                }
                else { // x1, x2, x0
                    n = abs( (LL)floor((x0-x1)/BB) ) - abs( (LL)floor((x0-x2)/BB));        
                    //printf("Case 3: n = %lld\n", n );    
                    LL k = (LL)(floor((x0-x2)/BB));    
                    if( equal(y2,floor(y2)) && equal(x2,x0-BB*k) && equal(y2,y0+AA*k) ){
                        if( !find(x0-BB*k,y0+AA*k) ) n++;    
                    }    
                    k = (LL)( floor(x0-x1)/BB );    
                    if( equal(y1,floor(y1)) && equal(x1,x0-BB*k) && equal(y1,y0+AA*k) ){
                        if( find(x0-BB*k,y0+AA*k) ) n--;    
                    }    
                }
            }
        }
        return n;
    }
    void Gao(){
        LL n = 0;    
        double x13 = (1.0*C1*B3-1.0*C3*B1)/(1.0*A1*B3-1.0*A3*B1);
        double x12 = (1.0*C1*B2-1.0*C2*B1)/(1.0*A1*B2-1.0*A2*B1);
        double x23 = (1.0*C2*B3-1.0*C3*B2)/(1.0*A2*B3-1.0*A3*B2);
        double y13 = (B1==0)?1.0*(C3-A3*x13)/B3:1.0*(C1-A1*x13)/B1;
        double y12 = (B1==0)?1.0*(C2-A2*x12)/B2:1.0*(C1-A1*x12)/B1;
        double y23 = (B2==0)?1.0*(C3-A3*x23)/B3:1.0*(C2-A2*x23)/B2;
        //printf("x13=%lf,y13=%lf, x12=%lf,y12=%lf,x23=%lf,y23=%lf\n",x13,y13,x12,y12,x23,y23);    
        mp.clear();    
        LL n1 = fun( x12,y12,x13,y13,A1,B1,C1 );
        LL n2 = fun( x23,y23,x12,y12,A2,B2,C2 );
        LL n3 = fun( x13,y13,x23,y23,A3,B3,C3 );
        printf("%lld\n", n1+n2+n3 );    
        //printf("n1=%lld,n2=%lld,n3=%lld\n", n1,n2,n3 );
    }
    int main(){
        int T;
        scanf("%d",&T);
        while( T-- ){
            scanf("%d%d%d",&A1,&B1,&C1);
            scanf("%d%d%d",&A2,&B2,&C2);
            scanf("%d%d%d",&A3,&B3,&C3);
            Gao();
        }
        return 0;
    }

    F Five Tiger

     纯模拟题, 没必要说太多. 不过要注意的是,  有点坑人的地方是,  四斜, 三斜只有给的那几种情况. 不要像我一样看复杂了~~~

    G Goddess

    神概率题, 不会.

    H Hurry Up

    初步分析下, 可以知道,  起点走到公路,再坐车到终点. 满足二元函数, 其中有个最小值. 那么直接三分求x点即可. 然后得到最优值与步行花费取个最小即可.

    I  I Love Military Chess

    签到题, 就不说了. 我的写法是 分成 {数字与数字},{数字与字母},{字母与数字},{字母与字母} 来讨论, 这样就不会漏掉了. 不过要注意特殊情况.判定.

    K Jack’s sequence

    其实分析下,就可以知道, 因为给定的 括号匹配串是合法的, 因为 左括号 小于 右括号. 要得到下一个,则必定是找个右括号与左括号交换.

    交换的前提还要保证, 整个括号串合法.  仔细推一下可以发现, 仅 ' (#()) ' 这种串才满足 左右括号交换后, 得到的串还满足要求.  其中'#' 表示一个合法串.

    那么要保证下一个. 而非多个.  则对于串  "(())()" 而言, 只有第4个位置的右括号能够与第二位的左括号交换, 然后得到合法串, 此时串形式为 " ()()() ", 

    但这个并不是 除原先的最小. 因为还有这种情况:  " ()(()) " ,   大概整理下.解法如下:

      找到最后一个形如 " ())"  则必定是交换 第一个 (, 与最后一个 ),  然后讨论后面的 串.  假定串为  ####())####,  则此时需要要考虑交换 (,)后, 后面的字符

    该如何排列以保证整个串最小.  直观的情况是  ()()() =>  ((())).  不难想到, 因为 左括号要比右括号小..

    还有个特别地方是,  " (((#()))() " => ((()((())) 

    View Code
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    using namespace std;
    
    const int N = 10010;
    char s[N];
    int main(){
        int T;
        scanf("%d", &T);
        while( T-- ){
            scanf("%s", s+1);
            bool flag = false;
            int len = strlen(s+1);
            for(int i = len; i >= 3; i-- ){
                if( s[i] == ')' ){
                    if( s[i-1] == ')' && s[i-2] == '(' ){
                        //printf("i = %d\n", i );
                        //getchar(); getchar(); getchar();
                        flag = true;        
                        for(int j = 1; j < i-2; j++) putchar(s[j]);    
                        putchar(')');    
                        if( (i+1<=len) && (s[i+1]==')') ){
                        //    putchar('('),putchar(')');
                            int k = i+1, L = 1;
                            while( (k<=len) && (s[k]==')') ) k++;// putchar(s[k++]);
                            if( k <= len ) L += (len-k+1)/2;
                            for(int j = 1; j <= L; j++) putchar('(');
                            for(int j = 1; j <= L; j++) putchar(')');
                            k = i+1;
                            while( (k<=len) && (s[k]==')') ) putchar(s[k++]);
                        }    
                        else{
                                
                            int L = (len-(i-1)+1)/2;
                            for(int j = 1; j <= L; j++) putchar('(');
                            for(int j = 1; j <= L; j++) putchar(')');
                        }    
                        puts("");    
                        break;    
                    }    
                }    
            }
            if( !flag ) puts("No solution");    
        }
        return 0;
    }
  • 相关阅读:
    Pyhon数据分析20——matplotlib可视化(二)之柱状图
    程序运行正常,数据库没反应
    Redis在Linux环境下安装的常见错误
    1.1-1.4 sqoop概述及安装cdh版hadoop
    3、css边框以及其他常用样式
    3.15-3.21 hive项目实战
    2、css的存在形式及优先级
    1、css选择器
    3.11-3.14 Hive 企业使用优化2
    3.7-3.10 Hive 企业使用优化1
  • 原文地址:https://www.cnblogs.com/yefeng1627/p/3074443.html
Copyright © 2011-2022 走看看