zoukankan      html  css  js  c++  java
  • 0729模拟赛解题报告

    4610 斗地主

     

     时间限制: 1 s
     空间限制: 256000 KB
     题目等级 : 大师 Master
     
     
     
    题目描述 Description

    牛牛最近迷上了一种叫斗地主的扑克游戏。 斗地主是一种使用黑桃、红心、梅花、
    方片的 A 到 K 加上大小王的共 54 张牌来进行的扑克牌游戏。在斗地主中, 牌的大小关
    系根据牌的数码表示如下: 3<4<5<6<7<8<9<10<J<Q<K<A<2<小王<大王, 而花色并不
    对牌的大小产生影响
    。 每一局游戏中,一副手牌由 n 张牌组成。游戏者每次可以根据规
    定的牌型进行出牌, 首先打光自己的手牌一方取得游戏的胜利。
    现在,牛牛只想知道,对于自己的若干组手牌, 分别最少需要多少次出牌可以将它
    们打光。 请你帮他解决这个问题。
    需要注意的是, 本题中游戏者每次可以出手的牌型与一般的斗地主相似而略有不同。
    具体规则如下:

    输入描述 Input Description

    输入文件名为 landlords.in。
    第一行包含用空格隔开的 2 个正整数T,n,表示手牌的组数以及每组手牌的张数。
    接下来T组数据,每组数据n行, 每行一个非负整数ai,bi,对表示一张牌, 其中ai
    示牌的数码,bi表示牌的花色,中间用空格隔开。 特别的, 我们用1 来表示数码 A 11 表
    示数码 J, 12 表示数码 Q, 13 表示数码 K;黑桃、红心、梅花、方片分别用 1-4 来表示; 小
    王的表示方法为 0 1, 大王的表示方法为 0 2

    输出描述 Output Description

    输出文件名为 landlords.out。
    共 T 行,每行一个整数,表示打光第i组手牌的最少次数。

    样例输入 Sample Input

    输入样例1

    1 8

    7 4
    8 4
    9 1
    10 4
    11 1
    5 1
    1 4

    1 1

    —————

    输入样例2


    1 17
    12 3
    4 3
    2 3
    5 4
    10 2
    3 3
    12 2
    0 1
    1 3
    10 1
    6 2
    12 1
    11 3

    5 2
    12 4
    2 2
    7 2

    样例输出 Sample Output

    输出样例1

    3

    —————

    输出样例2

    6

    数据范围及提示 Data Size & Hint

    测试点, 我们约定手牌组数 与张数 的规模如下:
    测试点编号  T    n

    1 100 2 |11 100 14
    2 100 2 |12 100 15
    3 100 3 |13 10 16
    4 100 3 |14 10 17
    5 100 4 |15 10 18
    6 100 4 |16 10 19
    7 100 10 |17 10 20
    8 100 11 |18 10 21
    9 100 12 |19 10 22
    10 100 13 |20 10 23

    分类标签 Tags 点此展开 

     
    暂无标签
    题解:
    神奇的A*,很玄学,具体看代码吧。
    AC代码:
    #include<cstdio>//A*
    #include<cstring>
    using namespace std;
    #define N 30
    int n,T,ans,a[N],c[N];
    int query(){//估价函数 
        memset(c,0,sizeof c);
        for(int i=0;i<=14;i++) c[a[i]]++;
        int tot=0;
        while(c[4]&&c[2]>1)  c[4]--,c[2]-=2,tot++;      
        while(c[4]&&c[1]>1) c[4]--,c[1]-=2,tot++;
        while(c[4]&&c[2]) c[4]--,c[2]--,tot++;
        while(c[3]&&c[2]) c[3]--,c[2]--,tot++;
        while(c[3]&&c[1]) c[3]--,c[1]--,tot++;
        return tot+c[1]+c[2]+c[3]+c[4]; //带牌+三张 对子 单张
    }
    void dfs(int now){
        if(now>=ans) return ;
        int tmp=query();
        if(now+tmp<ans) ans=now+tmp;
        for(int i=3;i<=14;i++){//三顺子
            int j=i;
            for(;a[j]>=3;j++);
            if(j-i>=2){
                for(int t=i+1;t<=j-1;t++){
                    for(int k=i;k<=t;k++) a[k]-=3;
                    dfs(now+1);
                    for(int k=i;k<=t;k++) a[k]+=3;
                }
            }
        }
        for(int i=3;i<=14;i++){//双顺子
            int j=i;
            for(;a[j]>=2;j++);
            if(j-i>=3){
                for(int t=i+2;t<=j-1;t++){
                    for(int k=i;k<=t;k++) a[k]-=2;
                    dfs(now+1);
                    for(int k=i;k<=t;k++) a[k]+=2;
                }
            }
        }
        for(int i=3;i<=14;i++){//单顺子
            int j=i;
            for(;a[j]>=1;j++);
            if(j-i>=5){
                for(int t=i+4;t<=j-1;t++){
                    for(int k=i;k<=t;k++) a[k]-=1;
                    dfs(now+1);
                    for(int k=i;k<=t;k++) a[k]+=1;
                }
            }
        }
    }
    int main(){
        scanf("%d%d",&T,&n);
        while(T--){
            memset(a,0,sizeof a);ans=1e9;
            for(int i=1,x,y;i<=n;i++){
                scanf("%d%d",&x,&y);
                if(x==1) x=14;//不会漏顺子
                a[x]++;
            }
            dfs(0);
            printf("%d
    ",ans);
        }
        return 0;
    }

    1064 虫食算

     

    2004年NOIP全国联赛提高组

     时间限制: 2 s
     空间限制: 128000 KB
     题目等级 : 钻石 Diamond
     
     
     
    题目描述 Description

     所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母。来看一个简单的例子:

           43#9865#045
        +    8468#6633
           44445506978

        其中#号代表被虫子啃掉的数字。根据算式,我们很容易判断:第一行的两个数字分别是5和3,第二行的数字是5。

        现在,我们对问题做两个限制:

        首先,我们只考虑加法的虫食算。这里的加法是N进制加法,算式中三个数都有N位,允许有前导的0。

        其次,虫子把所有的数都啃光了,我们只知道哪些数字是相同的,我们将相同的数字用相同的字母表示,不同的数字用不同的字母表示。如果这个算式是N进制的,我们就取英文字母表午的前N个大写字母来表示这个算式中的0到N-1这N个不同的数字:但是这N个字母并不一定顺序地代表0到N-1)。输入数据保证N个字母分别至少出现一次。



                BADC
          +    CBDA
                DCCC

        上面的算式是一个4进制的算式。很显然,我们只要让ABCD分别代表0123,便可以让这个式子成立了。你的任务是,对于给定的N进制加法算式,求出N个不同的字母分别代表的数字,使得该加法算式成立。输入数据保证有且仅有一组解,

    输入描述 Input Description

    输入包含4行。第一行有一个正整数N(N<=26),后面的3行每行有一个由大写字母组成的字符串,分别代表两个加数以及和。这3个字符串左右两端都没有空格,从高位到低位,并且恰好有N位。

    输出描述 Output Description

      输出包含一行。在这一行中,应当包含唯一的那组解。解是这样表示的:输出N个数字,分别表示A,B,C……所代表的数字,相邻的两个数字用一个空格隔开,不能有多余的空格。

    样例输入 Sample Input

    5
    ABCED
    BDACE
    EBBAA

    样例输出 Sample Output

    1 0 3 4 2

    数据范围及提示 Data Size & Hint

    对于30%的数据,保证有N<=10;
    对于50%的数据,保证有N<=15;
    对于全部的数据,保证有N<=26。

     

    曾经的错解:

    枚举全排列,挨个判断是不是正确答案。

    手打 40分

    stl   30分

    其余都TLE

    40分代码:

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<iostream>
    using namespace std;
    #define N 30
    char s1[N],s2[N],ss[N];
    int n,ys[N],a[N],b[N],c[N],d[N];
    bool vis[N];
    bool check(){
        int p=0;
        for(int i=n-1;i>=0;i--) a[++p]=ys[s1[i]-65];p=0;
        for(int i=n-1;i>=0;i--) b[++p]=ys[s2[i]-65];p=0;
        for(int i=1;i<=n;i++){
            c[i]+=a[i]+b[i];
            c[i+1]+=c[i]/n;
            c[i]%=n;    
        }
        for(int i=n-1;i>=0;i--) d[n-i]=ys[ss[i]-65];
        for(int i=n;i;i--) if(c[i]!=d[i]) return 0;
        return 1;
    }
    
    void dfs(int now){
        if(now==n){
            for(int i=0;i<=n;i++) c[i]=0;//当时忘了清零没结果40分变成10分了 
            //memset(c,0,sizeof(c));
            if(check()){
                for(int i=0;i<n;i++) printf("%d ",ys[i]);
                exit(0);
            }
            return ;
        }
        for(int i=0;i<n;i++){
            if(!vis[i]){
                vis[i]=1;
                ys[now]=i;
                dfs(now+1);
                vis[i]=0;
            }
        }
    }
    int main(){
        scanf("%d",&n);
        scanf("%s%s%s",s1,s2,ss);
        dfs(0);
        return 0;
    }

    正解:枚举全排列,并且加了来了两个剪枝

      ①当一列竖式中的字母已全部枚举,且不符合要求;

      ②当一列竖式中的字母已枚举2个,另一个数字已被使用;

      (剪枝时注意进位)

    AC代码:

    #include<cstdio>
    #include<cstring>
    #define N 27
    using namespace std;
    char s[4][N];
    int n,now;
    int a[N],b[N],c[N];
    bool goal;
    bool judge(){
        int temp=0,k=0;
        for(int i=n-1;i>=0;i--){
            temp=(b[s[1][i]-'A']+b[s[2][i]-'A']+k)%n;
            k=(b[s[1][i]-'A']+b[s[2][i]-'A']+k)/n;
            if(temp!=b[s[3][i]-'A']) return 0;
        }
        return 1;
    } 
    bool thrown(){//剪枝 
        int temp,t1,t2,t3;
        for(int i=n-1;i>=0;i--){
            t1=s[1][i]-'A',t2=s[2][i]-'A',t3=s[3][i]-'A';
            if(b[t1]!=-1&&b[t2]!=-1&&b[t3]!=-1){
                if((b[t1]+b[t2]+1)%n==b[t3]||(b[t1]+b[t2])%n==b[t3])continue; 
                return 0;
            }  
            if(b[t1]!=-1 && b[t2]!=-1){
                temp=(b[t1]+b[t2])%n;
                if(a[temp]==-1||a[(temp+1)%n]==-1)continue; 
                return 0;
            }
            if(b[t1]!=-1&&b[t3]!=-1){
                temp=b[t3]-b[t1];
                if(temp>=0&&a[temp]==-1)continue;
                temp+=n;
                if(temp>=0&&a[temp]==-1)continue;
                temp=b[t3]-b[t1]-1;
                if(temp>=0&&a[temp]==-1)continue;
                temp+=n;
                if(temp>=0&&a[temp]==-1)continue;
                return 0;
            }
            if(b[t2]!=-1 && b[t3]!=-1){
                temp=b[t3]-b[t2];
                if(temp>=0&&a[temp]==-1)continue;
                temp+=n;
                if(temp>=0&&a[temp]==-1)continue;
                temp=b[t3]-b[t2]-1;
                if(temp>=0&&a[temp]==-1)continue;
                temp+=n;
                if(temp>=0&&a[temp]==-1)continue;
                return 0;
            }
        }
        return 1;
    }
    void dfs(int k){
        if(k>n){
            if(judge()) goal=1;
            return;
        }
        for(int i=n-1;i>=0;i--){
            if(a[i]==-1){
                a[i]=c[k];
                b[c[k]]=i;
                if(thrown()) dfs(k+1);
                if(goal) return;
                a[i]=-1;
                b[c[k]]=-1;
            }
        }    
    }
    int main()
    {
        scanf("%d",&n);
        scanf("%s",s[1]);
        scanf("%s",s[2]);
        scanf("%s",s[3]);
        memset(a,-1,sizeof(a));
        for(int i=n-1;i>=0;i--){
            for(int j=1;j<=3;j++){
                if(!b[s[j][i]-'A']){
                    b[s[j][i]-'A']=-1;
                    c[++now]=s[j][i]-'A';
                }
            }
        }    
        dfs(1);
        for(int i=0;i<n;i++) printf("%d ",b[i]);
        return 0;
    }

    1074 食物链

     

    2001年NOI全国竞赛

     时间限制: 3 s
     空间限制: 64000 KB
     题目等级 : 钻石 Diamond
     
     
     
    题目描述 Description

    动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形。A吃B,B吃C,C吃A。   

    现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。   

    有人用两种说法对这N个动物所构成的食物链关系进行描述:   

    第一种说法是“1 X Y”,表示X和Y是同类。   

    第二种说法是“2 X Y”,表示X吃Y。   

    此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。   

    1) 当前的话与前面的某些真的话冲突,就是假话;   

    2) 当前的话中X或Y比N大,就是假话;   

    3) 当前的话表示X吃X,就是假话。   

    你的任务是根据给定的N(1<=N<=50,000)和K句话(0<=K<=100,000),输出假话的总数。

    输入描述 Input Description

    第一行是两个整数N和K,以一个空格分隔。   

    以下K行每行是三个正整数D,X,Y,两数之间用一个空格隔开,其中 D 表示说法的种类。   

    若D=1,则表示X和Y是同类。   

    若D=2,则表示X吃Y。

    输出描述 Output Description

    只有一个整数,表示假话的数目。

    样例输入 Sample Input

    100 7

    1 101 1

    2 1 2

    2 2 3

    2 3 3

    1 1 3

    2 3 1

    1 5 5

    样例输出 Sample Output

    3

    数据范围及提示 Data Size & Hint

    输入文件  

     对7句话的分析 100 7

    1 101 1  假话

    2 1 2    真话

    2 2 3    真话

    2 3 3    假话

    1 1 3    假话

    2 3 1    真话

     1 5 5    真话

    NOI 2001 食物链(eat)

    #include<cstdio>
    using namespace std;
    int n,m,ans,fa[301000];
    inline int read(){
        register int x=0,f=1;
        register char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
        return x*f;
    } 
    int find(int x){
        return fa[x]==x?x:fa[x]=find(fa[x]);
    }
    inline void merge(int x,int y){
        fa[find(x)]=find(y);
    }
    inline bool same(int x,int y){
        return find(x)==find(y);
    }
    int main(){
        n=read();m=read();
        for(int i=1;i<=n*3;i++) fa[i]=i;
        for(int i=1,x,y,z;i<=m;i++){
            z=read();x=read();y=read();
            if(x>n||y>n){ans++;continue;}
            if(z==1)
                if(same(x,y+n)||same(x,y+n*2)) ans++;
                else merge(x,y),merge(x+n,y+n),merge(x+n*2,y+n*2);
            else
                if(same(x,y)||same(x,y+n*2)) ans++;
                else merge(x,y+n),merge(x+n,y+n*2),merge(x+n*2,y);
        }
        printf("%d
    ",ans);
        return 0;
    }
  • 相关阅读:
    蓝牙的HFP协议笔记
    23种设计模式
    读QT5.7源码(三)Q_OBJECT 和QMetaObject
    实现私有化(Pimpl) --- QT常见的设计模式
    蓝牙Profile的概念和常见种类(转)
    git分支合并
    git log的常见用法
    QThread详解
    git查看某个文件的修改历史
    因为代理原因导致的NotSerializableException
  • 原文地址:https://www.cnblogs.com/shenben/p/5722573.html
Copyright © 2011-2022 走看看