• Noip2007提高组总结


      两道基础题,后两题比较麻烦,算法想出来后,还是一些细枝末节的问题,需要特别注意,感觉Noip的题目质量还是挺高的,每做一套,都感觉会有大大小小不同的收获,就要月考了,最后把07年的题目总结一下,算是这两天的收获……

    T1:统计数字

      没有任何悬念的练习题,排序然后输出……

    #include <cstdio>
    #include <algorithm>
    int n,a[200005],pos;
    using namespace std;
    int main(){
        scanf("%d",&n);
        for(int i=0;i<n;i++)scanf("%d",&a[i]);
        sort(a,a+n); pos=0;
        printf("%d ",a[0]);
        for(int i=1;i<=n;i++)if(a[i]!=a[i-1]){
            printf("%d
    ",i-pos),pos=i;
            if(a[i])printf("%d ",a[i]);
        }
        return 0;
    }
    

    T2:字符串的展开

      字符串处理的基础题,主要考察读题的仔细程度和编程的素质,尽量代码短一些,那么也好调试一点。

    #include <cstdio>
    #include <cstring>
    using namespace std;
    #define rep() for(int j=0;j<p2;j++)
    char s[1000000];
    int p1,p2,p3;
    void pr1(int b,int e){for(int i=b;i<=e;i++)rep()printf("%c",(char)i);}
    void pr2(int b,int e){for(int i=e;i>=b;i--)rep()printf("%c",(char)i);}
    void pr3(int b,int e){for(int i=b;i<=e;i++)rep()printf("%c",'*');}
    int main(){
        scanf("%d%d%d",&p1,&p2,&p3);
        scanf("%s",s); printf("%c",s[0]);
        for(int i=1;i<strlen(s);i++){
            if(s[i]!='-'){printf("%c",s[i]);continue;} 
            if(p3==1){
                if(s[i-1]>='0'&&s[i+1]<='9'&&s[i-1]<s[i+1]){
                    if(p1==3)pr3(int(s[i-1])+1,int(s[i+1])-1);
                    else pr1(int(s[i-1])+1,int(s[i+1])-1); continue;
                }
                if(s[i-1]>='a'&&s[i+1]<='z'&&s[i-1]<s[i+1]){
                    if(p1==1)pr1(int(s[i-1])+1,int(s[i+1])-1);
                    else if(p1==2)pr1(s[i-1]-'a'+'A'+1,s[i+1]-'a'+'A'-1);
                    else pr3(int(s[i-1])+1,int(s[i+1])-1);
                    continue;
                }
            }else{
                if(s[i-1]>='0'&&s[i+1]<='9'&&s[i-1]<s[i+1]){
                    if(p1==3)pr3(int(s[i-1])+1,int(s[i+1])-1);
                    else pr2(int(s[i-1])+1,int(s[i+1])-1); continue;
                }
                if(s[i-1]>='a'&&s[i+1]<='z'&&s[i-1]<s[i+1]){
                    if(p1==1)pr2(int(s[i-1])+1,int(s[i+1])-1);
                    else if(p1==2)pr2(s[i-1]-'a'+'A'+1,s[i+1]-'a'+'A'-1);
                    else pr3(int(s[i-1])+1,int(s[i+1])-1);
                    continue;
                }
            }printf("%c",s[i]);
        }
        return 0;
    }
    

    T3:矩阵取数游戏

      这道题的Dp方程还是比较好推的,就是从中间向两端扩展,然后搜索进去层层外推即可,方程是f[i][j]=max{f[i][j-1]+a[j],f[i+1][j]+a[i]},然后是2的幂次问题,因为直接算幂的话比较慢,所以用霍纳规则,每一次出栈时乘2,就可以了,没打高精度60分:

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    typedef long long LL;
    LL ans,a[81],f[81][81];
    LL getans(int s,int t){
        if(f[s][t])return f[s][t];
        if(s==t)return f[s][s]=2*a[s];
        LL t1,t2;
        t1=getans(s+1,t)+a[s];
        t2=getans(s,t-1)+a[t];
        if(t1>t2)return f[s][t]=2*t1;
        else return f[s][t]=2*t2;
    }
    int main(){
        int n,m;
        scanf("%d%d",&n,&m);
        while(n--){
            for(int i=1;i<=m;i++)scanf("%d",&a[i]);
            memset(f,0,sizeof f);
            ans+=getans(1,m);
        }
        cout<<ans;
        return 0;
    }
    

      之后粘上高精度的,重载运算符版本……

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    typedef struct bigint{
        int le,u[81];
        bigint(){le=0;memset(u,0,sizeof(u));}
        bigint operator+(const bigint &a){
            int l=max(le,a.le);
            bigint m;
            for(int i=0;i<=l;i++){
                m.u[i]+=u[i]+a.u[i];
                m.u[i+1]+=m.u[i]/10;
                m.u[i]%=10;
            }
            if(m.u[l+1]!=0)m.le=l+1;
            else m.le=l;
            return m;
        }
        bool operator>(const bigint &a){
            if(le>a.le)return true;
            if(le<a.le)return false;
            for(int i=le;i>=0;i--){
                if(u[i]>a.u[i])return true;
                if(u[i]<a.u[i])return false;
            }
            return false;
        }
        friend ostream& operator<<(ostream &cout,bigint a){
            for(int i=a.le;i>=0;i--)
            cout<<a.u[i];
            return cout;
        }
        friend istream& operator>>(istream &cin,bigint &a){
            int i,l;char b[82];
            cin>>b;
            memset(a.u,0,sizeof(a.u));
            a.le=l=strlen(b)-1;
            for(int i=0;i<=l;i++)a.u[l-i]=b[i]-'0';
            return cin;
        }
        bool empty(){
            if(le==0&&u[0]==0)return true;
            else return false;
        }
    };
    bigint a[81],f[81][81];
    int n,m;
    bigint ans;
    bigint getans(int s,int t){
        if(!f[s][t].empty())return f[s][t];
        if(s==t)return f[s][s]=a[s]+a[s];
        bigint t1,t2;
        t1=getans(s+1,t)+a[s];
        t2=getans(s,t-1)+a[t];
        if(t1>t2)return f[s][t]=t1+t1;
        else return f[s][t]=t2+t2;
    }
    int main(){
        cin>>n>>m;
        for(int i=1;i<=n;i++){
            memset(f,0,sizeof(f));
            for(int j=1;j<=m;j++)cin>>a[j];
            ans=ans+getans(1,m);
        }
        cout<<ans;
        return 0;
    }
    

    T4:树网的核

      对于这道题目首先要做到的是找树的直径,可以得出如果有多条直径的话,随便选取一条就可以了,因为越靠近核一定更优,选取链越长一定越优,然后就是实际操作的问题了,一开始因为忘记了pl数组在再次搜索中会被重记而导致WA,特别注意……

    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <iostream>
    using namespace std;
    int l[305],m,vis[305],head[305],u[605],v[605],w[605],next[605];
    bool start;
    int tmp,ans,pl[305],now[305],tt,max,pre[305],lis[305],top,ss;
    void build(){  
        memset(head,-1,sizeof(head)); 
        for(int e=1;e<=m;e++){  
            scanf("%d%d%d",&u[e],&v[e],&w[e]);  
            u[e+m]=v[e]; v[e+m]=u[e]; w[e+m]=w[e];  
            next[e]=head[u[e]]; head[u[e]]=e;  
            next[e+m]=head[u[e+m]]; head[u[e+m]]=e+m;  
        }  
    } 
    void del(int begin,int end){for(int u=begin;u<=end;u++)vis[lis[u]]=1;}
    queue Q;
    void bfs(int x){
        int i,j,k; 
        vis[x]=1; Q.push(x);
        while(!Q.empty()){
            int tot=Q.front(); Q.pop();
            for(int e=head[tot];~e;e=next[e])if(!vis[v[e]]){
                vis[v[e]]=1;
                if(start)pre[v[e]]=tot,pl[v[e]]=w[e];
                Q.push(v[e]);
                l[v[e]]=l[tot]+w[e];
            }
        } 
    }
    int main(){
        int n,x,y,z,k;
        start=1;
        scanf("%d%d",&n,&k); m=n-1;
        build();
        bfs(1); int max=0;
        for(int i=1;i<=n;i++)if(l[i]>max){max=l[i];tt=i;}
        memset(l,0,sizeof l);
        memset(vis,0,sizeof vis);
        memset(pl,0,sizeof pl);
        memset(pre,0,sizeof pre);
        bfs(tt); max=0; 
        start=0;
        for(int i=1;i<=n;i++)if(l[i]>max){max=l[i];ss=i;}
        int po=ss;
        do{lis[top++]=po;po=pre[po];}while(lis[top-1]!=tt);
        ans=~0U>>1;
        for(int i=0;i<top;i++){
            int nl=0,j=i,tmp=0;
            while((j+1)<top&&nl+pl[lis[j]]<=k)nl+=pl[lis[j++]];
            for(int u=i;u<=j;u++){
                max=0;
                memset(l,0,sizeof l);
                memset(vis,0,sizeof vis);
                del(i,j); bfs(lis[u]);
                for(int y=1;y<=n;y++)if(l[y]>max)max=l[y];
                if(max>tmp)tmp=max;
            }
            if(tmp<ans)ans=tmp;
        }
        printf("%d
    ",ans);
        return 0;
    }
    

     注意点:

      1.链表开双向边的时候一定要记得开两倍以上的数组;

      2.先考虑暴力的情况的,然后再一步步优化,不一定一开始就想标解;

      3.在编程时注意初始化,而且对于一些要用的量,一定要注意是否因为后来的操作而改变了,就像T4的错误

     

  • 相关阅读:
    P2704 [NOI2001]炮兵阵地[状压dp]
    【CRT】中国剩余定理简介
    乘法逆元
    P2921 [USACO08DEC]在农场万圣节[SCC缩点]
    P1967 货车运输[生成树+LCA]
    P2746 P2812 [USACO5.3]校园网Network of Schools[SCC缩点]
    Debian初始化配置
    Linux网桥配置
    Jenkins与Gitlab集成
    Gitlab搭建
  • 原文地址:https://www.cnblogs.com/forever97/p/Noip2007.html
Copyright © 2020-2023  润新知