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

 

posted @ 2013-05-12 20:38  yefeng1627  阅读(305)  评论(0编辑  收藏  举报

Launch CodeCogs Equation Editor