2013-5-12 训练赛后总结

 感谢 谢大,duoxida,zhsl ,教会了我几个训练赛当中没有想出来的题。

 

A Force Brute

题意:  没发现其水题本质啊..   给定 N个单词, 然后问最大循环次数. 

解法:   KMP  next数组的运用, 更详细的可以看这一篇总结 http://www.cnblogs.com/yefeng1627/archive/2013/04/28/3050027.html

这里就简要说下,  因为求next的过程是一个一个构造循环节的, (L+1)-next[L+1] 即为当前字符串的最小循环节长度, 而 L%( (L+1)-next[L+1] ) 表示目前 循环节构造了多少个,若为0则意味着构造满了一个循环.  而 L / ( (L+1) - next[L+1] )  表是目前已经构造了的循环节数量.  所以最终结果为   

  令 d = (L+1) - next[L+1] ,   则 ans  = L/d + (L%d != 0) 

若 next[L+1] = 1, 则代表整个串是 最小循环节. 其实发现可以不特殊处理的.

#include<cstdio>

#include<cstring>

#include<cstdlib>

#include<map>

#include<string>

#include<algorithm>

using namespace std;



const int N = (int)1e5+10;



char str[N*25], s[30];

int nxt[N], a[N], top, n;



map<string,int> mp;

int main(){

    int T;

    scanf("%d", &T); getchar();

    while( T-- ){

        gets(str);

        top = n = 0;

        mp.clear();    

        char *p = strtok( str, " " );

        while( p ){

            if( mp.count(p) == 0 )    mp[p] = ++top;    

            a[n++] = mp[p];

            p = strtok( NULL, " " );

        }

    //    for(int i = 0; i < n; i++)

    //        printf("%d ", a[i] ); puts("");

        int i = 0, j = 1; nxt[1] = 0;

        while( j <= n ){

            if( i == 0 || a[i-1] == a[j-1] )

                nxt[++j] = ++i;

            else    i = nxt[i];

        }

    //    for(int i = 1; i <= n+1; i++)

    //        printf("%d ", nxt[i] ); puts("");

        if( nxt[n+1] != 1 ){

            int d = (n+1)-nxt[n+1];

            printf("%d\n", n/d + (n%d!=0) );

        }     

        else puts("1");    

    }

    return 0;

}
View Code

 

 

 

 

B God Lin’s Harem

题意:  N个点,每个点有个权值, 操作主要是,修改单点,删除单点,查询单点,查询最值下标,相同则输出最近的。

解法: 线段树即可, 初始便建立N个顶点,因为最多N次操作。 然后对于N操作,每次给其一个优先级,这样在查询时,

出现权值相同,则取优先级大的。 对于更新和修改操作。单点更新直接到底就好。查询操作O(1).

顺便说下,更新操作是不影响优先级的。别像我一样,在这里WA好久。

View Code
#include<cstdio>

#include<cstring>

#include<cstdlib>

#include<string>

#include<algorithm>

#include<queue>

#include<map>

using namespace std;

typedef long long LL;



#define lch rt<<1,l,m

#define rch rt<<1|1,m+1,r

const int N = (int)1e5+100;



LL tree[N<<2];

int idx[N<<2], cnt[N];

int n, top;



void push_up(int rt){

    int lc = rt<<1, rc = rt<<1|1;

    if( (tree[lc]>tree[rc]) || ((tree[lc]==tree[rc])&&(cnt[idx[lc]]>cnt[idx[rc]]) ) )

        tree[rt] = tree[lc], idx[rt] = idx[lc];

    else tree[rt] = tree[rc], idx[rt] = idx[rc];

}

void build(int rt,int l,int r){

    tree[rt] = -1; idx[rt] = l;

    if( l == r ){ return; }

    int m = (l+r)>>1;

    build( lch ), build( rch );

    push_up( rt );

}

void update(int rt,int l,int r,int a,LL b, bool flag){

    if( l == r ){

        if(flag) tree[rt] = b;

        else tree[rt] += b;

        return;    

    }

    int m = (l+r)>>1;

    if(a <= m) update( lch, a, b,flag );

    else update( rch, a, b,flag );

    push_up(rt);

}

int main(){

    freopen("1.in","r",stdin);    

    while( scanf("%d", &n) != EOF ){

        if( n == 0 ) break;    

        char op[2];

        int x, y, k = 1;

        bool flag = true;

        top = 0;    

        memset( cnt, 0, sizeof(cnt));    

        

        build( 1, 1, n  ); //print();

        

        for(int i = 0; i < n; i++){

            scanf("%s", op);

            switch( op[0] ){

                case 'N': 

                    scanf("%d",&x); cnt[k] = ++top;

                    update(1,1,n,k++,x,true);

                    break;

                case 'I': 

                    scanf("%d%d",&x,&y);

                    if( cnt[x] == -1 ) continue;    

                    //cnt[x] = ++top;

                    update(1,1,n,x,y,false); break;

                case 'D': 

                    scanf("%d%d",&x,&y); 

                    if( cnt[x] == -1 ) continue;    

                    //cnt[x] = 0;    

                    //    cnt[x] = ++top;

                    update(1,1,n,x,-y,false); break;

                case 'E':

                    scanf("%d", &x); cnt[x] = -1;

                    update(1,1,n,x,-1,true); break;

                case 'S':

                    if( flag ) flag = false;

                    else printf(" ");

                    printf("%d", idx[1] );

            } 

        }    

        puts("");    

    }

    return 0;

}

 

C Harry Potter

题意: 一个人从0点出发,走到X,每次只能走X+1, 或者X*2. 问最小时间。X <= 10^18.

解法: X小可以用BFS搜索, 但是这里其实可以不用,基于贪心的原则,走最大理论上是最优,但若从前面走则不一定。

那么我们从X走到0,每次走 X/2 ,若X是奇数则 X-1, 这样即为最终结果。

View Code
#include<cstdio>

typedef long long LL;



int main(){

    int T;

    scanf("%d", &T);

    while( T-- ){

        LL x, t = 0;

        scanf("%lld", &x);

        while( x ){

            if( x&1 ) x--;

            else x /= 2;

            t++;    

        }

        printf("%lld\n", t );    

    }

    return 0;

}

顺便贴个BFS版本,不过这里A不了的

View Code
#include<cstdio>

#include<map>

#include<queue>

#include<algorithm>

using namespace std;

typedef long long LL;



const LL inf = (LL)1e18+10;





struct node{

    LL x, t;

}pre,nxt;



queue<node> Q;

map<LL,int> mp;



LL solve(LL X){

    while( !Q.empty() ) Q.pop();

    mp.clear();

    pre.x = 0, pre.t = 0;

    mp[ pre.x ] = 1;

    Q.push( pre );

    while( !Q.empty() ){

        pre = Q.front(); Q.pop();

        if( pre.x == X ) return pre.t;

        if( pre.x+1 < inf ){

            if( mp.count(pre.x+1) == 0 ){    

                if(pre.x+1==X) return pre.t+1;        

                nxt.x = pre.x+1; nxt.t = pre.t+1;

                Q.push( nxt );

                mp[ nxt.x ] = 1;    

            }    

        }

        if( pre.x*2 < inf ){

            if( mp.count(pre.x*2) == 0 ){

                if( pre.x*2 == X ) return pre.t+1;

                nxt.x = pre.x*2; nxt.t = pre.t+1;

                Q.push( nxt );

                mp[ nxt.x ] = 1;

            }    

        }    

    }

}

int main(){

    int T;

    scanf("%d", &T);

    while( T-- ) {

        LL K;    

        scanf("%lld", &K); 

        printf("%lld\n", solve(K) );    

    }

    return 0;

}

 

D  (LCS)^2

题意: 题目很直白啊,两个长度为10^6的串,求最长公共子序列,输出。

解法: 表示不会, O(NlogN)的算法

 

 

E N皇后问题

题意:  没看明白

 

F 查询成绩

题意:  给两个串, s,t , 其中t中部分为字符'*', 问t是否为S的片段。strlen( s, t ) <= 110

解法:  递归匹配,即可。 若  Tj = '*', 则Tj+1 与 Si开始匹配,  若Tj != '*' ,则比较 Tj ,Si.  注意多个*在一起时,只算一个*

View Code
#include<cstdio>

#include<cstring>

#include<string>

#include<algorithm>

using namespace std;



char s[150], t[150];

int la, lb;

int find( int x, int y ){

    if( (y<lb)&&(x>=la) ) return 0;

    if( y == lb ) return 1;    

    if( t[y] == '*' ){    

        while( t[y+1] == '*' ) y++;    

        int tmp;    

        for(int i = x; i < la; i++){

            if( s[i] == t[y+1] ){ 

                tmp = find( i+1, y+2 );                    

                if(tmp) return 1;    

            }    

        }

        return 0;    

    }    

    else{

        if( s[x] != t[y] ) return 0;

        else return find( x+1, y+1 );

    }

}



int main(){

    while( scanf("%s %s", s, t+1 ) != EOF){

        t[0] = '*';    

        la = strlen(s), lb = strlen(t);    

        puts( find(0,0) ? "yes" : "no" );    

    }

    return 0;

}

 

 

G 卡片重组

题意:  N张带有权值的卡片,顺序摆放,先要求分成多份,并且每份单调,不能交换位置,单分中权值为sum(i,j)*num(i,j) 。

解法:  动态规划  

  dp( i ) 表示前 i 张卡片最大权值,转移方程为

  dp( i ) = Max{  dp(j ) + Sum(j+1,i)* Num( j+1,i)  }   其中  j < i,  且 ( j+1, i ) 区间单调

View Code
#include<cstdio>

#include<cstdlib>

#include<cstring>

#include<algorithm>

using namespace std;



typedef long long LL;

const int N = 1010;



int dp[N], val[N], sum[N];

int n;



int main(){

    while( scanf("%d", &n) != EOF){

        for(int i = 1; i <= n; i++)

            scanf("%d", &val[i] );

        sum[0] = 0;

        for(int i = 1; i <= n; i++) sum[i] = sum[i-1]+val[i];

        memset( dp, 0, sizeof(dp));

        for(int i = 1; i <= n; i++){

            dp[i] = dp[i-1] + val[i];

            int j = i-2; 

            while( (j>=0) && (val[j+1]>val[j+2]) )

                dp[i] = max( dp[i], dp[j] + (i-j)*(sum[i]-sum[j]) ), j--;

            j = i-2;

            while( (j>=0) && (val[j+1]<val[j+2]) )

                dp[i] = max( dp[i], dp[j] + (i-j)*(sum[i]-sum[j]) ), j--;

        }

        printf("%d\n", dp[n] );    

    }    

    return 0;

}

 

H 括号匹配

题意: 给一括号串 ( ?? ) , 其中 ? 可为 ( 或者 ), 问合法方案数

解法: 动态规划

  因为括号匹配则,将 左括号 "(" 看作1, 右括号")"看作-1,则合法的括号串其所有前缀和都 >= 0.

  又因为串总长为 1000, 则其前缀和最大为 500. 

  状态方程 dp ( i, j )  表示 前i个括号,最后一个前缀和为 j的 方案数 (之前的都满足>=0)

  转移方程为:

    Si = '(' ,  则前缀和+1,  dp( i, j+1 ) += dp( i-1, j ) ,   其中 j+1 <= 500

    Si = ')',   则前缀和-1,     dp( i, j-1 )  += dp( i-1,j ),     其中 j-1 >= 0 

    Si = '?',   则前缀和可能+1,也可-1, 同样要满足上面 j+1 <= 500, j-1 >= 0

        dp( i, j+1 ) += dp( i-1, j )

        dp( i, j-1 ) += dp( i-1, j )

View Code
#include<cstdlib>

#include<cstdio>

#include<cstring>



typedef long long LL;

const int Mod = (int)1e9+7;



const int N = 1010;

const int M = 500;

LL dp[N][1200];



char s[N];



int main(){

    while( scanf("%s", s+1) != EOF){

        int n = strlen(s+1);    

        memset(dp,0,sizeof(dp));

        dp[0][0+M] = 1;

        for(int i = 1; i <= n; i++){

            for(int j = 0; j <= 500; j++){

                if( (s[i] == '(')&&(j+1<=500) )    

                    dp[i][ (j+1)+M ] = (dp[i][(j+1)+M]+dp[i-1][j+M])%Mod;

                else if( (s[i] == ')') && (j-1>=0) )

                    dp[i][ (j-1)+M ] = (dp[i][(j-1)+M]+dp[i-1][j+M])%Mod;

                else if( s[i] == '?') {

                    if( j+1 <= 500 )    

                        dp[i][ (j+1)+M ] = (dp[i][(j+1)+M]+dp[i-1][j+M])%Mod;

                    if( j-1 >= 0 )    

                        dp[i][ (j-1)+M ] = (dp[i][(j-1)+M]+dp[i-1][j+M])%Mod;

                }

            }    

        }    

        printf("%lld\n", dp[n][M] );    

    }    

    return 0;

}

 

 

I 排列的逆序数

题意:  {1,2...n}的所有排列中逆序数为k的排列个数。

解法:  动态规划

    状态方程 dp( i, j )  表示前 i个数,逆序数为 j 的排列数,  因为最大逆序数为  n*(n-1)/2,  当n = 100, 则lim 接近5000

    转移方程  dp( i, j+k  ) +=  dp( i-1, j ) , 其中  k表示第i位贡献的逆序数, 取值范围为  [0, n-i], 并且 前i个数的最大逆序数量为  n*i - i*(i+1)/2,

所以 j <= n*i - i*(i+1)/2. 

View Code
#include<cstdio>

#include<cstring>

#include<cstdlib>

const int mod = (int)1e9+7;



typedef long long LL;



LL dp[110][5010];



int n, K;



int main(){

    while( scanf("%d%d", &n,&K) != EOF){    

        int lim = n*(n-1)/2;    

        memset( dp, 0, sizeof(dp));

        dp[0][0] = 1;    

        for(int i = 1; i <= n; i++){    

            int cnt = i*n-(1+i)*i/2;    

            for(int j = 0; j <= cnt; j++){    

                if( dp[i-1][j] == 0 ) continue;    

                for(int k = 0; k <= n-i; k++)

                    if( j+k <= lim )

                        dp[i][j+k] = (dp[i][j+k]+dp[i-1][j])%mod;

            }

        }

        printf("%lld\n", dp[n][K] );    

    }

    return 0;

}

 

J 物品选择

题意: N件物品,都有个价值,部分是主件,部分是附件,附件需要在主件被选情况下才可选择。

解法: 背包

  首先分组, 假定有 m个主件, 每个主件有 ni个附件。

  令 g( i, j ) 表示 从第i个主件中,取 j个物品的最大价值 , 其中  j <= ni,   g(i,0)看作只去主件本身 

  则令 dp( i , j ) 表示前 i个主件 ,取J个物品的最大价值, 转移方程为:

    dp( i , j ) = Max{ dp(i-1, j),  dp( i-1, j-(k+1) ) + g(i, k) }   //其中 j - (k+1) >= 0,  k <= mi 

  然后 g( i, j ) 求法也差不多。

View Code
#include<cstdio>

#include<cstdlib>

#include<vector>

#include<algorithm>

#include<cstring>

using namespace std;



const int N = 1010;



vector<int> Q[1010];



int g[N][N], d[N][N], dp[N][N], a[N][N];

int n, K;

int val[N], v[N], L[N];

bool flag[N];

int sum[N];



int main(){

    while( scanf("%d%d", &n,&K) != EOF){

        memset(flag,0,sizeof(flag));

        for(int i = 0; i <= n; i++) Q[i].clear();

        for(int i = 1; i <= n; i++){

            int p;    

            scanf("%d%d",&val[i],&p);

            if( p != i ) Q[p].push_back(val[i]);

            flag[p] = true;    

        }    

        memset(g,0,sizeof(g));

        for(int x = 1; x <= n; x++){ // 第x件物品,其为主件

            int len = (int)Q[x].size();    

            if( flag[x] ){

                // 0,1 pack    

                memset(d,0,sizeof(d));    

                d[0][0] = val[x];    

                for(int i = 1; i <= len; i++){

                    for(int j = 0; j <= i; j++){

                        if( j == 0 ) d[i][j] = d[i-1][j];        

                        else if( j == i ) d[i][j] = d[i-1][j-1] + Q[x][i-1];    

                        else d[i][j] = max( d[i-1][j], d[i-1][j-1]+ Q[x][i-1] );    

                    }    

                }    

                for(int i = 0; i <= len; i++)

                    g[x][i] = d[len][i];

            }    

        }

        int cnt = 0;

        for(int i = 1; i <= n; i++){

            if( flag[i] ){

                L[++cnt] = Q[i].size();

                for(int j = 0; j <= L[cnt]; j++)

                    a[cnt][j] = g[i][j];

            }    

        }

        memset( dp,0, sizeof(dp));    

        for(int i = 1; i <= cnt; i++){

            for(int j = 0; j <= K; j++){

                dp[i][j] = dp[i-1][j];

                if( j > 0 ){    

                    for(int k = 0; (k<=L[i]) && (k<j); k++){

                        dp[i][j] = max( dp[i][j], dp[i-1][j-(k+1)] + a[i][k] );    

                    }    

                }    

            }    

        }    

        printf("%d\n", dp[cnt][K] );    

    }    

    return 0;

}

 

你可能感兴趣的:(总结)