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;
}
原文地址:https://www.cnblogs.com/shenben/p/5722573.html