zoukankan      html  css  js  c++  java
  • 【NOIP2006】提高组

    T1能量项链

    题目链接

    这一届的T1终于不是模拟题了,改成了一道环形dp。刚开始没有考虑好环形的细节导致WA了一半点(还是弱啊QAQ)。说说正解吧:f[l][r]表示把l到r之间的能量珠合成一颗所释放的最大能量,那么我们就可以通过枚举中间点k,f[l][r]=max(f[l][r],f[l][k]+he[l]*ta[k]*ta[r]+f[k+1][r])。但是由于是环形,我们还要把1-n再储存一遍,这样的话答案就是f[i][i+n-1](1<=i<=n)的最大值了。最后注意一下循环嵌套枚举的顺序就好了。

    时间复杂度O(n3),很容易就跑过了。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int f[205][205],he[205],ta[205];
    int main()
    {
        int n,mxa=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&he[i]);
            he[i+n]=he[i];
            if(i==1)ta[n]=he[i],ta[i+n-1]=ta[n];
            else ta[i-1]=he[i],ta[i+n-1]=he[i];
            f[i][i]=0;f[i+n][i+n]=0;
        }
        for(int i=n*2-1;i>=1;i--){
            for(int j=i+1;j<=i+n-1&&j<=n*2;j++){
                for(int k=i;k<j;k++){
                    f[i][j]=max(f[i][j],f[i][k]+he[i]*ta[k]*ta[j]+f[k+1][j]);
                }
            }
        }
        for(int i=1;i<=n;i++)
        mxa=max(mxa,f[i][i+n-1]);
        printf("%d",mxa);
        return 0;
    }
    T1

    T2金明的预算方案

    题目链接

    其实这就是一道要分情况的01背包问题,决策变成了购买主件、购买主件和附件1、购买主件和附件2以及购买主件和两件附件4种。因为所有的价格都是10的倍数,所以可以把所有的钱数在一开始就除以10,到最后输出答案是再乘回去,这样能有效降低时间复杂度和空间复杂度。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int p1[65],p2[65],v[65],v1[65],v2[65],p[65];
    bool ok[65];
    int f[32001];
    int main()
    {
        int n,vi,pi,qi,m,mxa=0;
        scanf("%d %d",&n,&m);n/=10;
        for(int i=1;i<=m;i++){
            scanf("%d %d %d",&vi,&pi,&qi);
            vi/=10;
            if(qi){
                ok[i]=1;
                if(!v1[qi]){v1[qi]=vi;p1[qi]=pi;}
                else {v2[qi]=vi;p2[qi]=pi;}
            }
            else{v[i]=vi;p[i]=pi;}
        }
        for(int i=1;i<=m;i++){
            if(ok[i])continue;
            for(int j=n;j>=v[i];j--){
                f[j]=max(f[j],f[j-v[i]]+v[i]*p[i]);
                if(j>=v[i]+v1[i])f[j]=max(f[j],f[j-v[i]-v1[i]]+v[i]*p[i]+v1[i]*p1[i]);
                if(j>=v[i]+v2[i])f[j]=max(f[j],f[j-v[i]-v2[i]]+v[i]*p[i]+v2[i]*p2[i]);
                if(j>=v[i]+v1[i]+v2[i])f[j]=max(f[j],f[j-v[i]-v1[i]-v2[i]]+v[i]*p[i]+v1[i]*p1[i]+v2[i]*p2[i]);
                mxa=max(mxa,f[j]);
            }
        }
        printf("%d",mxa*10);
        return 0;
    }
    T2

     T3作业调度方案

    题目链接

    这一年的模拟题放在了第三题,这道题传说也是NOIP中最考语文的题目(我看了几遍还是很懵,最后不得不上网看翻译才理解好题意)。抛开文字游戏不说,这道题其实不是很难,贪心的思路也在要求中说了出来,只需要按照顺序模拟即可。因为数据范围很弱,因此可以肆意用空间啦。开一个bool数组ok[i][j]表示第i台机器在第j秒时是否为空闲状态。我们需要把目前的操作放在这个工件前一个工序之后尽量靠前的位置,值得注意的是一个操作完成的那一刻即可马上开始下一个操作,即认为那一刻机器处于空闲状态。剩下的就是要注意打标记和消除标记的细节问题。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int q[505],ta=0,now[25];
    int p[25][25],t[25][25],tt[25];
    bool ok[25][505],f;
    int main()
    {
        int n,m,mx=0;
        scanf("%d %d",&m,&n);
        for(int i=1;i<=n;i++)now[i]=1;
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++)
                scanf("%d",&q[++ta]);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d",&p[i][j]);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d",&t[i][j]);
        for(int i=1;i<=ta;i++){
            int x=q[i],y=p[x][now[x]],t0=t[x][now[x]];
            for(int j=tt[x];;j++){int j0=j;
                if(ok[y][j])continue;f=1;
                for(int k=j;k<j+t0;k++){
                    if(ok[y][k]){j=k;f=0;break;}
                    ok[y][k]=1;
                }
                if(!f){for(int k=j0;k<j;k++)ok[y][k]=0;}
                else{tt[x]=j0+t0;mx=max(mx,tt[x]);break;}
            }
            now[x]++;
        }
        printf("%d",mx);
        return 0;
    }
    T3

     T4 2^k进制数

    题目链接

    这道题我一开始是很懵逼的,后来看了黄学长的题解和代码才幡然醒悟。在这里直接引用黄学长的题解:

    题目中的那个从另一角度分析就已经蕴含了这个题的基本思路。就以题目的例子为例,长度为7位的01字串按3位一段就这样分:0 000 000。其中除了首段,每段都小于(111)2,也即小于2k,而首段自然是小于2w%k(对于w%k为0时也成立)了。

    如果首段为0,则当这个2k进制数位数分别为2、3、…、[n/k]时,如果用b_max表示2k,对应的解的个数分别为C[b_max-1][2]、C[b_max-1][3]、…、C[b_max-1][n/k](C[i][j]表示从i个数里选j个构成一组组合)。

    如果首段不为0,设首段为x,则解就有c[b_max-x-1][n/k]个。

    这样,求解的个数就搞定了,剩下的活就是高精了。求组合数可以用这个公式:C[n][m]=C[n-1][m-1]+C[n-1][m],这样高精就只用加法了。

    代码(基本就是黄学长的思路):

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int shu[205];
    int c[520][520][105];
    int k,w,wei;
    void plus1(int x[],int y[],int z[])
    {
        z[0]=max(x[0],y[0]);
        for(int i=1;i<=z[0];i++){
            z[i]+=x[i]+y[i];
            z[i+1]+=z[i]/10;
            z[i]%=10;
        }
        if(z[z[0]+1]>0)z[0]++;
    }
    void plus2(int x[],int y[])
    {
        x[0]=max(x[0],y[0]);
        for(int i=1;i<=x[0];i++){
            x[i]+=y[i];
            x[i+1]+=x[i]/10;
            x[i]%=10;
        }
        if(x[x[0]+1]>0)x[0]++;
    }
    int main()
    {
        scanf("%d %d",&k,&w);
        int bx=1<<k;
        int ax=1<<(w%k);
        for(int i=0;i<=bx;i++){
            for(int j=0;j<=i;j++){
                if(j==0)c[i][0][0]=c[i][0][1]=1;
                else plus1(c[i-1][j-1],c[i-1][j],c[i][j]);
            }
        }
        for(int i=2;i<=w/k&&i<bx;i++)plus2(shu,c[bx-1][i]);
        for(int i=1;i<ax&&i+w/k<bx;i++)plus2(shu,c[bx-i-1][w/k]);
        for(int i=shu[0];i>=1;i--)printf("%d",shu[i]);
        return 0;
    }
    T4
  • 相关阅读:
    POJ训练计划1035_Spell checker(串处理/暴力)
    软考-数据库与标准化和知识产权
    分析Cocos2d-x横版ACT手游源码 1、公共
    增强版的RecycleViewAdapter,能够直接使用
    高考志愿,你们想好怎么填了吗?
    win32收不到F10按键消息解决的方法
    Json——使用Json jar包实现Json字符串与Java对象或集合之间的互相转换
    九度OJ #1437 To Fill or Not to Fil
    SQL数据分组后取最大值或者取前几个值(依照某一列排序)
    3.5星|《硅谷产品》:Facebook网红社区产品经理经验谈
  • 原文地址:https://www.cnblogs.com/JKAI/p/7299888.html
Copyright © 2011-2022 走看看