• 2017 清北济南考前刷题Day 7 morning


    期望得分:100+50+20=170

    实际得分:10+50+20=80

    1. 纸牌

    题目描述

    在桌面上放着n张纸牌,每张纸牌有两面,每面都写着一个非负整数。你的邪王真眼可以看到所有牌朝上的一面和朝下的一面写的数字。现在你需要将一些牌翻过来,使得所有牌朝上的一面中,至少有一半(≥n/2)的数字是一样的。请你求出最少需要翻几张牌,或者判断无解。

    注意:在翻牌的时候,你不能把牌扔掉,不能偷偷把别的牌放进来,也不能用笔涂改牌上面的数字。

     

    输入格式

             第一行包含一个整数n,表示牌的数量;

             接下来n行,每行两个非负整数ai, bi,表示每张牌上写的两个数字,ai对应朝上的一面,bi对应朝下的一面。

     

    输出格式

             如果有解,则输出一个整数,表示最少的翻牌次数,否则输出Impossible。

     

    样例输入1

    3

    1 2

    2 1

    3 4

     

    样例输出1

    1

     

    样例解释1

             把第一张牌翻过来,那么就有两个2一个3朝上了,2的数量超过了半数。

     

    样例输入2

    3

    1 2

    3 4

    5 6

     

    样例输出2

    Impossible

     

    样例解释2

             所有数字都只有一个,因此相同的数字数超过半数是不可能的。

     

    最多只有四种数的个数会超过n/2,

    所有数先排序,然后枚举,当一个数的个数为n/2时,就用这个数来更新答案

    注意:如果正反都为同一个数,这个数只能算1个

     

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    #define N 300001
    
    int a[N],b[N],c[N<<1];
    
    void read(int &x)
    {
        x=0; char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    }
    
    int main()
    {
        freopen("card.in","r",stdin);
        freopen("card.out","w",stdout);
        int n,cnt=0;
        read(n);
        for(int i=1;i<=n;++i)
        {
            read(a[i]); c[++cnt]=a[i];
            read(b[i]); if(a[i]!=b[i]) c[++cnt]=b[i];
        }
        sort(c+1,c+cnt+1);
        int ans=n+1,sum=0,m=n+1>>1;
        for(int i=1;i<=cnt;++i)
        {
            if(c[i]==c[i-1])
            {
                ++sum;
                if(sum==m)
                {
                    int tot=0;
                    for(int j=1;j<=n;++j)  if(a[j]==c[i]) tot++;
                    tot=min(tot,m);
                    ans=min(ans,m-tot);
                }        
            }
            else sum=1;
        }
        if(ans>n) printf("Impossible");
        else printf("%d",ans);
    }
    View Code

    2. 后缀数组

    题目描述

             给定一个字符串S,它的长为n,后缀数组的功能是,将其所有后缀按字典序从小到大排好序。我们对其做一点小小的改动:再给定一个数字m,记ssi表示从S的第i位开始、长度最多为m的子串,我们希望将这些字符串{ssi}按字典序从小到大排序。举个栗子,当S="abcab",m=2时,ssi的值分别为:

             ss1="ab"

             ss2="bc"

             ss3="ca"

             ss4="ab"

             ss5="b"                  

             但是,只是把{ssi}全部排好序还是太简单了。初始状态下,ss1~ssn按顺序排成一行,我们只能通过不断交换某两个相邻字符串的位置来做排序。再举个栗子,把上面提到的ss1~ss5排好序的一种方案是:

    (0)     原序列:"ab", "bc", "ca", "ab", "b"

    (1)     交换第3和第4个串:"ab", "bc", "ab", ca", "b"

    (2)     交换第2和第3个串:"ab", "ab", "bc", ca", "b"

    (3)     交换第4和第5个串:"ab", "ab", "bc", b", "ca"

    (4)     交换第3和第4个串:"ab", "ab", "b", bc", "ca"

    现在,你需要求出,最少通过多少次相邻字符串交换,才能把所有子串{ssi}排成字典序从小到大的形式。

             ( ´_ゝ`)NOIP怎么可能会考后缀数组

    输入格式

             第一行包含两个整数n和m;

             第二行包含字符串S,它的长为n,只包含小写字母。

    输出格式

             一个整数,表示最少交换次数。

    样例输入

    5 2

    abcab

    样例输出

    4

    样例解释

             样例就是题目描述中提到的例子。

    数据范围

             对于20%的数据,有n≤10;

             对于40%的数据,有n≤100;

             对于60%的数据,有n≤5000;

             另有10%的数据,有m≤5;

             另有10%的数据,S是随机生成的;

             对于100%的数据,有1≤m≤n≤50000

     

    把所有子串排好序,答案就是逆序对的个数

    注意处理相同子串

    唯一的问题是如何对所有子串排序

     

    正解思路:

    哈希排序

    即归并排序字符串,用二分+哈希值找出两个字符串第一个不同的字母

    #include<cstdio>
    #include<iostream>
    
    #define N 50001
    
    const int mod=10000019;
    
    int n,m,d;
    
    char s[N];
    
    long long has[N],bit[N];
    
    int a[N],b[N];
    
    long long ans;
    
    int GetHash(int l,int r)
    {
        int tmp;
        if(r<=n) 
        {
            tmp=has[r]-has[l-1]*bit[r-l+1]%mod;
            if(tmp<0) tmp+=mod;
        }
        else
        {
            tmp=has[n]-has[l-1]*bit[n-l+1]%mod;
            if(tmp<0) tmp+=mod;
            tmp=1LL*bit[r-n]%mod;
        }
        return tmp;
    }
    
    int cmp(int i,int j)
    {
        int l=1,r=m,mid,tmp=-1;
        while(l<=r)
        {
            mid=l+r>>1;
            if(GetHash(i,i+mid-1)!=GetHash(j,j+mid-1)) tmp=mid,r=mid-1;
            else l=mid+1;
        }
        if(tmp==-1) return 0;
        char x= i+tmp-1>n ? ' ' : s[i+tmp-1];
        char y= j+tmp-1>n ? ' ' : s[j+tmp-1];
        if(x<y) return -1;
        return 1;
    }
    
    void gbsort(int l,int r)
    {
        if(l==r) return;
        int mid=l+r>>1;
        gbsort(l,mid);
        gbsort(mid+1,r);
        int i=l,j=mid+1,k=l;
        while(i<=mid && j<=r)
        {
            d=cmp(a[i],a[j]);
            if(d<=0) b[k++]=a[i++];
            else if(d>0) b[k++]=a[j++],ans+=mid-i+1;
        }
        //printf("%d %d %I64d
    ",l,r,ans);
        while(i<=mid) b[k++]=a[i++];
        while(j<=r) b[k++]=a[j++];
        for(k=l;k<=r;++k) a[k]=b[k];
    }
    
    int main()
    {
        freopen("sort.in","r",stdin);
        freopen("sort.out","w",stdout);
        scanf("%d%d",&n,&m);
        scanf("%s",s+1);
        bit[0]=1;
        for(int i=1;i<=n;++i) bit[i]=bit[i-1]*27%mod;
        has[1]=s[1]-'a'+1;
        for(int i=2;i<=n;++i) has[i]=(has[i-1]*27+(s[i]-'a'+1))%mod;
        for(int i=1;i<=n;++i) a[i]=i;
        gbsort(1,n);
        std::cout<<ans;
    }
    View Code

     

    考场思路:

    假设当前比较每个子串的第i位

    把第i位为a的放到一个vector里,b放到一个vector里……

    继续递归下去

    时间复杂度 O(n^2 * 26)

    那个26 可以去掉,但 每一层递归都要开26的vector,实测 更慢

    不用vector 会MLE

     

    50分代码

    #include<cstdio>
    #include<vector>
    #include<algorithm>
    #include<iostream>
    #define lowbit(x) x&-x
    
    using namespace std;
    
    int n,m,cnt;
    
    #define N 50002
    
    char s[N];
    
    int rank[N],sum[N];
    
    int c[N];
    
    void mysort(int dep,vector<int>v)
    {
        int siz=v.size();
        if(!siz) return;
        if(dep==m)
        {
            for(int i=0;i<siz;i++)
                rank[v[i]]=cnt+1;
            cnt++;
            return;
        }
        vector<int>b; 
        int mi=27,mx=-1;
        for(int i=0;i<siz;i++)
        {
            if(v[i]+dep==n+1)  rank[v[i]]=++cnt; 
            else mi=min(mi,s[v[i]+dep]-'a'),mx=max(mx,s[v[i]+dep]-'a');
        }
        for(int i=mi;i<=mx;i++)
        {
            b.clear();
            for(int j=0;j<siz;j++)
                if(v[j]+dep<=n && s[v[j]+dep]-'a'==i) b.push_back(v[j]);
            mysort(dep+1,b);
        }
    }
    
    void add(int x)
    {
        while(x<=cnt)
        {
            c[x]++;
            x+=lowbit(x);
        }
    }
    
    int query(int x)
    {
        int tot=0;
        while(x)
        {
            tot+=c[x];
            x-=lowbit(x);
        }
        return tot;
    }
    
    int main()
    {
    //    freopen("sort.in","r",stdin);
    //    freopen("sort.out","w",stdout);
        scanf("%d%d",&n,&m);
        scanf("%s",s+1);
        vector<int>ve;
        for(int i=1;i<=n;i++) ve.push_back(i);
        mysort(0,ve);
        for(int i=1;i<=n;i++) sum[rank[i]+1]++;
        for(int i=1;i<=n;i++) sum[i]+=sum[i-1];
        //for(int i=1;i<=n;i++) printf("%d ",rank[i]);
        //printf("
    ");
        //for(int i=1;i<=cnt;i++) printf("%d ",sum[i]);
        //printf("
    ");
        long long ans=0;
        for(int i=1;i<=n;i++)
        {
            ans+= rank[i]==1 ? sum[rank[i]] : sum[rank[i]]-query(rank[i]-1);
            add(rank[i]);
        }
        cout<<ans;
    }
    View Code

     

    3. 巧克力

    题目描述

             有一块分成n*m个格子的矩形巧克力,虽然形状上很规整但质量分布并不均匀,每一格有各自的重量,用n*m个正整数表示。你需要将这一整块巧克力切成k小块,要求每块都是矩形,且它们的重量分别为a1~ak。一块巧克力的重量等于它包含的所有格子的重量之和。

             切巧克力的时候,你可以每次选一块大的巧克力,沿着某条格线横向或纵向将其切成两块小的巧克力。切下来的小块巧克力可以继续切割。切割路线不能是折线或斜线。任何时候当前的所有巧克力块都必须是矩形的。

             对于给定的巧克力和分割要求,请你判断是否存在一个切割方案满足上述要求。

    输入格式

             输入包含多组测试数据。输入文件的第一行包含一个正整数T,表示数据组数;

             接下来,每组测试数据的第一行包含3个正整数n, m, k,表示巧克力的长、宽以及它需要被切成多少块;

             接下来n行,每行m个正整数,第i行第j个数wi,j表示巧克力第i行第j列那一格的重量;

             接下来一行包含k个正整数a1~ak,表示要求切成的每块巧克力的重量。

    输出格式

             输出T行,表示对每组测试数据的判断结果。如果第i组测试数据存在一种切割方案,则在第i行输出"yes",否则输出"no"。

    样例输入

    2

    3 3 4

    1 2 3

    4 5 6

    7 8 9

    12 16 8 9

    2 2 2

    1 1

    1 1

    1 3

    样例输出

    yes

    no

    状压

    50%的数据每个格子的w=1

    f[i][j][s] 表示用 状态为s的巧克力 能否拼出 i*j的的矩形

    记忆化搜索 枚举横切、竖切

    根据面积计算新的长宽 

    100%的数据待填

    20暴力

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    int T,n,m,k;
    
    int w[11][11],a[16];
    
    void read(int &x)
    {
        x=0; char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    }
    
    void init()
    {
        read(n); read(m); read(k);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                read(w[i][j]);
        for(int i=1;i<=k;i++) read(a[i]);
    }
    
    namespace solve1
    {
        int pos[11],tot[11];
        
        bool ok;
        
        void judge()
        {
            memset(tot,0,sizeof(tot));
            for(int i=1;i<=k-1;i++)
                for(int j=pos[i-1]+1;j<=pos[i];j++)
                    tot[i]+=w[1][j];
            for(int j=pos[k-1]+1;j<=m;j++) tot[k]+=w[1][j];
            sort(tot+1,tot+k+1);
            for(int i=1;i<=k;i++)
                if(tot[i]!=a[i]) return;
            ok=true;
        }
        
        void dfs(int sum,int last)
        {
            if(ok) return;
            if(sum==k) { judge(); return; }
            for(int i=last+1;i+k-1-sum<m;i++)
            {
                if(sum==1 && i==2)
                {
                    int esa=3;
                }
                pos[sum]=i;
                dfs(sum+1,i);
                if(ok) return;
            }
        }
        
        void work()
        {
            sort(a+1,a+k+1);
            ok=false;
            dfs(1,0);
            puts(ok ? "yes" : "no");
        }
    }
    
    namespace solve2
    {
        void work()
        {
            int sum=0;
            for(int i=1;i<=k;i++)  sum+=a[i];
            if(sum>n*m) puts("no");
            else puts("yes");
        }
    }
    
    int main()
    {
        freopen("chocolate.in","r",stdin);
        freopen("chocolate.out","w",stdout);
        read(T);
        while(T--)
        {
            init();
            if(n==1) solve1::work();
            else solve2::work();
        }
    }
    View Code
  • 相关阅读:
    Linux c 获取cpu使用率(2)
    Linux c codeblock的使用(四):创建自己的静态函数库
    Linux c codeblock的使用(三):使用函数库
    Linux c codeblock的使用(二):在工程中编译多个文件
    Linux c codeblock的使用(一):新建一个工程
    关于warning: suggest parentheses around assignment used as truth value [-Wparentheses]|的解决方法
    Linux c使用gumbo库解析页面表单信息(三)
    Linux c获取任意路径的硬盘使用情况
    Linux c使用gumbo库解析页面表单信息(二)
    Linux c使用gumbo库解析页面表单信息(一)
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/7779118.html
Copyright © 2020-2023  润新知