2013-5-11 湘潭多省程序设计 赛后总结

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

 

posted @ 2013-05-12 19:34  yefeng1627  阅读(281)  评论(0编辑  收藏  举报

Launch CodeCogs Equation Editor