• HDnoip2017题解


    那么,作为一名初入信息竞赛的选手,我也试着开始用博客记录自己的学习历程,那么这篇文章先简单介绍一下我自己吧。

    本人开始学习信息学大概以来,主要都是用的C++,所以对其他语言并不是十分熟悉。2016我还只是一名NOIP普及组的选手,水掉一个一等奖后美滋滋继续往下学。最近刚刚搞完今年HDNIOIP提高组前,听同学说最后一道题是省选第二题的难度后我懵逼了(由于最近刚比完如果想要题解可以搜索“xjr01”, hdNOIP2017 题解 -> " http://www.cnblogs.com/xiao-ju-ruo-xjr/ "),其实第一篇文章也不知道写什么,那就胡乱写一下普及组的题解吧(无聊)。

    那么首先来看第一题

    无脑暴力,哈希前缀和什么的随便写,就不解释了。

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #define LL long long
    #define M 2020
    using namespace std;
    int n,m,k,a[M],ans,x,y,tmp;
    int need(int x){
        if(x%k==0) return x/k;
        return x/k+1;
    }
    int main(){
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=m;i++){
            scanf("%d%d",&x,&y);
            a[x]++;
            a[y]--;
        }
        for(int i=1;i<=n+1;i++){
            tmp+=a[i];
            ans=max(ans,need(tmp));
        }
        ans=min(ans,need(m));
        printf("%d",ans);
        return 0;
    }

    第二题,一个简单的动态规划

    设 f [ i ][ j ][ 0 ]表示时刻 i 时耗费了 j 的体力来到a树,f [ i ][ j ][ 1 ]表示时刻 i 时耗费了 j 的体力来到b树。

    转移:

    f [ i ][ j ][ 1 ]可以从 f [ i-1 ][  j-2 ][ 0 ]和 f [ i-1 ][ j ][ 1 ]转移

    f [ i ][ j ][ 0 ]可以从 f [ i-1 ][  j-1 ][ 1 ]和 f [ i-1 ][ j ][ 0 ]转移

    (至于为什么你们自己想)

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #define LL long long
    #define M 300
    using namespace std;
    int n,m,f[M][M][2],x,a[M],b[M],ans;
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        for(int i=1;i<=n;i++){
            scanf("%d",&b[i]);
        }
        f[1][0][0]=a[1];
        f[1][0][1]=b[1];
        for(int i=2;i<=n;i++){
            for(int j=0;j<=m;j++){
                f[i][j][0]=f[i-1][j][0];
                f[i][j][1]=f[i-1][j][1];
                if(j>1) f[i][j][1]=max(f[i-1][j-2][0],f[i][j][1]);
                if(j>0) f[i][j][0]=max(f[i-1][j-1][1],f[i][j][0]);
                f[i][j][0]+=a[i];
                f[i][j][1]+=b[i];
            }
        }
        for(int i=0;i<=m;i++){
            ans=max(ans,max(f[n][i][0],f[n][i][1]));
        }
        printf("%d",ans);
        return 0;
    }

    然后是第三题

    第三题大概是比较复杂的一道题了,关键就在于如何处理全排列的序号。
    这道题一共分为两部分

    第一部分,将给定的全排列转化成全排列的序号。

    首先对于一个k,他的全排列一共有k!(k的阶乘)种排列。

    设置一个变量 cnt=0,s[n]存储这个排列;

    对于第 i 位,若有 k 个 j 满足: i < j <= n 且 s[ j ] > s[ i ],则我们需要将 cnt 加上 ( n - i )!* k。

    为什么呢?对于某一个长为 N 的排列 , 一共分为 N 个部分,第 i 个部分是以 第 i 小的数为开头的排列,且这N个部分都有(N-1)!个排列。

    也就是说,我们对于每一位,从这一位到结尾都看做一个未离散化的排列(依靠每一个数的大小关系把他们看做一个不是很严谨的排列) ,然后求这个排列在这些数“全排列”中的哪个部分,也就求得了需要从0向后跳个部分才能达到当前的部分。

    这样我们就求得了给出序列的序号(编号)

    我们将m加上cnt,得到k,就得到了最后应该输出的排列的编号。

    第二部分,输出给定编号的全排列。

    读到这里,你应该已经明白了,对于第 i 位,我们只要用 k 除以(n-i)的阶乘,就知道这个序列应该是位于第m个部分,然后输出在剩余的未输出过的数中第m小的数即可

    所以说,我们只需要一个阶乘的预处理,然后瞎搞就好了。

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #define LL long long
    #define M
    using namespace std;
    LL n,m,s[22],p[22];
    bool f[22];
    LL cnt(LL x){
        LL co=0;
        for(LL i=x+1;i<=n;i++){
            if(p[i]<p[x]) co++;
        }
        return co;
    }
    LL check(LL x){
        LL co=0;
        for(LL i=1;i<=n;i++){
            if(f[i]) continue;
            co++;
            if(co==x){
                f[i]=true;
                return i;
            }
        }
        return -1;//这句话毫无意义
    }
    int main(){
        s[0]=1;
        scanf("%lld%lld",&n,&m);
        memset(f,true,sizeof(f));
        for(LL i=1;i<=n;i++) s[i]=s[i-1]*i;
        for(LL i=1;i<=n;i++) scanf("%lld",&p[i]),f[i]=false;
        for(LL i=1;i<=n;i++){
            m+=cnt(i)*s[n-i];
        }
        for(LL i=1;i<=n;i++){
            if(i>1) printf(" ");
            printf("%lld",check(m/s[n-i]+1));
            m%=s[n-i];
        }
        return 0;
    }

    第四道题,是一个特殊的二叉树,数字由于n<=10,我就只写了一个比较好些的但是比较慢的程序。

    这个程序大概是这样,由于在先序遍历中,子节点一定在父节点后才出现,每一个节点的右子节点(如果有的话)总在它父节点的左子节点后出现,所以我只是暴力建树,枚举每个点的父节点,建完树之后用中序遍历检验建的这棵树是否正确即可,如果即可,再直接递归的计算。

    然而,万万没想到,我最后还是栽了一个点应为这道题有^的操作(这里的^是指次方,而不是异或),我这里就暂时不写高精度了。

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<stack>
    #define LL long long
    #define M 20
    using namespace std;
    LL n,m,p[M],f[M],l[M],r[M],c[M],cnt;
    string a,b;
    
    bool isd(char x){
        if(x>='0'&&x<='9') return true;
        return false;
    }
    int take(char x){
        if(isd(x)) return x-'0';
        if(x=='+') return -1;
        if(x=='-') return -2;
        if(x=='*') return -3;
        return -4;
    }
    bool search(LL x){
        if(p[x]>=0){
            cnt++;
            if(p[x]==c[cnt]) return true;
            return false;
        }
        if(l[x]==0||r[x]==0) return false;
        if(!search(l[x])) return false;
        cnt++;
        if(p[x]!=c[cnt]) return false;
        if(!search(r[x])) return false;
        return true; 
    }
    LL pow(LL x,LL y){
        if(y==0) return 1;
        return x*pow(x,y-1);
    }
    LL calc(LL x){
        if(p[x]>=0) return p[x];
        if(p[x]==-1) return calc(l[x])+calc(r[x]);
        if(p[x]==-2) return calc(l[x])-calc(r[x]);
        if(p[x]==-3) return calc(l[x])*calc(r[x]);
        return pow(calc(l[x]),calc(r[x])); 
    } 
    void dfs(LL x){
        if(x==n+1){
            cnt=0;
            if(search(1)){
                printf("%lld",calc(1));
                exit(0);
            }
            return;
        }
        for(LL i=x-1;i>0;i--){
            if(p[i]>=0) continue;
            if(l[i]==0){
                l[i]=x,f[x]=i;
                dfs(x+1);
                l[i]=0,f[x]=0;
            }
            else if(r[i]==0){
                r[i]=x,f[x]=i;
                dfs(x+1);
                r[i]=0,f[x]=0;
            }
        }
    }
    int main(){
        scanf("%lld",&n);
        cin>>a;
        cin>>b;
        f[1]=1;
        f[2]=1;
        for(LL i=1;i<=n;i++){
            p[i]=take(a[i-1]);
            c[i]=take(b[i-1]);
        }
        dfs(2);
        return 0;
    }

    至于提高组的题解,欢迎大家访问http://www.cnblogs.com/xiao-ju-ruo-xjr/ 

  • 相关阅读:
    [原]Eclipse 安装SVN、Maven插件(补充)
    [原]几个云笔记的简单比较
    [原]Unity3d中奇怪的编译错误
    [原]unity中WWW isDone方法只能在主线程中调用
    C语言。格式化符号
    Unity iOS Guideline 1.3
    AR增强现实 之Metaio For Unity 开发 之HelloWorld
    Unity 4.6 bate 20 or 4.5.5 +vuforia3.0.9 发布到真机错误 解决
    服务器端Ajax异步分页类,基本通用分页类
    从数据库中读取数据并写入到Excle电子表格之2
  • 原文地址:https://www.cnblogs.com/OYJason/p/7608106.html
Copyright © 2020-2023  润新知