UVALive 5907 —— Tichu(搜索)

来源:互联网 发布:达内培训 武汉java 编辑:程序博客网 时间:2024/05/17 00:05

题目在此


题意:前面废话忽略,重点是,手上初始有13张牌(输入),然后按照一定的规则可以将牌组合起来,每张牌必须且只能用一次;

问题是要找一种方案使得组合出来的组数应尽量少,输出最少多少组,并输出任意一个方案。

每张牌上面有点数2~9 T J Q K A,规定点数按照这个顺序增大,即2是最小, A是最大的,花色用s,h,c,d代表;

题目给出6种组合规则:

1、单一张牌;

2、两张点数一样的牌;

3、三张点数一样的牌;

4、四张点数一样的牌;

5、三张点数为x和两站点数为y,x和y不同,当然相同你也找不到5张。。。

6、点数连续至少5张,如6789TJ,其实就是按照上面的点数大小顺序来;

题目保证输入的牌不会有重复。


采用搜索来解这个题目,貌似不用怎么剪枝速度也是很快的。

看起来有13张牌,逐张搜索的话每层理论是6种决策,也就是总状态数6^13,但事实上并不会出现这么多。因为你每次组合都会引发其他牌数量的减少,也就是说其实收敛得很快的,像规则5和6一次至少都去掉5张牌了。

我的做法是用一个数组f记录每种点数的张数,再用一个布尔的数组p,p[i][j]为1代表点数为i的牌出现了第j种花色(这个是为了后面输出方案)。

点数方面,为了后面枚举方便,将上面的点数按顺序映射到0~12,点数2在0位置,A在12位置,下面说到的点数都是位置了。

上面看上去是6种方案,实际可以把前四点看作一种:对于点数x,枚举取1~f[x]张牌。

单种点数的枚举和三飞二的没什么,主要是连续的枚举,假如有0~11都可以连续得到,如果先枚举0~4,接着0~5,0~6等等,个人觉得是比较麻烦了。

我的做法是直接用i从x往右扫,直到i==13或是f[i]为0,break出来,那么说明x最多连续到i-1,前面在扫的时候对那些非0都先减一;

接下来就从x~i往回枚举了,如果i-x<4就说明不足5张,不用递归,否则就递归下去,递归完了,要判断x~i-1了,此时先将f[i]++,因为现在是不取i了。(可能有点绕,看代码可能好懂些)

在搜索的时候,每次选择一种决策方案的时候,先把选择的点数做对应的修改(减少),花色不用管,决策没管到花色,然后递归进入下一层,回溯的时候在把点数加回去即可。

方案的记录,我是采用一个结构体

struct Solu{    int A, B, C;    Solu(){}    Solu(int _A, int _B, int _C){        A=_A; B=_B; C=_C;    }}

A代表方案类型,具体如下:

A=0时:表示连续的B到C,比如6789TJ,就表示为A=0,B=4,C=9(这里记录的还是点数映射的位置)

A=1时:表示点数为B取了C张;

A=2时:表示点数为B取了三张,点数C的取了两张;

然后剩下的工作就是写好搜索函数,寻找并记录最优答案了。

最后面的输出,就通过分析结构体内A的类型做对应的输出即可。因为题目对方案的顺序没要求,所以你就随意咯,怎么输出方便就怎么来。

具体细节请参考代码:

#include<cstdio>#include<cstring>const int inf = 0x7fffffff;char s[10];char list[20]={'2','3','4','5','6','7','8','9','T','J','Q','K','A'};char type[10]={'s','h','c','d'};void getch(char a, char b, int& x, int& y){    for(int i=0; i<13; i++){        if(list[i]==a){            x=i;            break;        }    }    for(int i=0; i<4; i++){        if(type[i]==b){            y=i;            break;        }    }}int f[13];bool p[13][4];void print(int x){    putchar(list[x]);    //选择一个存在的花色输出,并把花色标记为0    for(int i=0; i<4; i++){        if(p[x][i]){            putchar(type[i]);            p[x][i]=0;            break;        }    }}struct Solu{    int A, B, C;    Solu(){}    Solu(int _A, int _B, int _C){        A=_A; B=_B; C=_C;    }}cur[50], res[50];//cur记录当前的方案,res记录当前的最优方案int t, ans;void dfs(int x, int u){    if(u>=ans)  return;//小小的剪枝,如果当前组数超过全局最优则返回    if(x>=13){        if(u<ans){//更新答案            ans = u;            for(int i=0; i<u; i++)  res[i] = cur[i];        }        return;    }    //当前点数张数为0,直接看下一个点数    //并且这样的写法,保证了搜索到x时,x前面的点数一定是空的或是被取完了    if(!f[x]){        dfs(x+1, u);        return;    }    int i;    //找向右的连续最大区间    for(i=x; i<13; i++){        if(!f[i])   break;        f[i]--;    }    for(--i; i>=x; i--){        if(i-x>=4){//连续超过5张的            cur[u] = Solu(0, x, i);            dfs(x, u+1);        }        f[i]++;    }    for(i=1; i<=f[x]; i++){        //同种点数取i张        f[x]-=i;        cur[u] = Solu(1, x, i);        dfs(x, u+1);        f[x]+=i;    }    if(f[x]>=3){        //取三张x和两张i        f[x]-=3;        for(i=x+1; i<13; i++){            if(f[i]>=2){                f[i]-=2;                cur[u] = Solu(2, x, i);                dfs(x, u+1);                f[i]+=2;            }        }        f[x]+=3;    }    if(f[x]>=2){        //取三张i和两张x        f[x]-=2;        for(i=x+1; i<13; i++){            if(f[i]>=3){                f[i]-=3;                cur[u] = Solu(2, i, x);                dfs(x, u+1);                f[i]+=3;            }        }        f[x]+=2;    }}int main(){    scanf("%d", &t);    while(t--){        int x, y;        memset(f,0,sizeof(f));        memset(p,0,sizeof(p));        for(int i=0; i<13; i++){            scanf("%s", s);            getch(s[0], s[1], x, y);            f[x]++;            p[x][y]=1;        }        ans = inf;        dfs(0,0);        printf("%d\n", ans);        for(int i=0; i<ans; i++){            if(res[i].A==0){                for(int j=res[i].B; j<=res[i].C; j++){                    if(j!=res[i].B) putchar(' ');                    print(j);                }            }            else if(res[i].A==1){                for(int j=0; j<res[i].C; j++){                    if(j)   putchar(' ');                    print(res[i].B);                }            }            else{                print(res[i].B);                for(int j=0; j<2; j++){                    putchar(' '); print(res[i].B);                    putchar(' '); print(res[i].C);                }            }            puts("");        }    }    return 0;}



0 0
原创粉丝点击