[NOIP2015] 斗地主
NOIP2015 斗地主
时间限制: 3 Sec 内存限制: 1024 MB题目描述
牛牛最近迷上了一种叫斗地主的扑克游戏。斗地主是一种使用黑桃、红心、梅花、方片的A到K加上大小王的共54张牌来进行的扑克牌游戏。在斗地主中,牌的大小关系根据牌的数码表示如下:3<4<5<6<7<8<9<10<J<Q<K<A<2<小王<大王,而花色并不对牌的大小产生影响。每一局游戏中,一副手牌由n张牌组成。游戏者每次可以根据规定的牌型进行出牌,首先打光自己的手牌一方取得游戏的胜利。现在,牛牛只想知道,对于自己的若干组手牌,分别最少需要多少次出牌可以将它们打光。请你帮他解决这个问题。需要注意的是,本题中游戏者每次可以出手的牌型与一般的斗地主相似而略有不同。具体规则如下:
输入
第一行包含用空格隔开的2个正整数T,N,表示手牌的组数以及每组手牌的张数。
接下来T组数据,每组数据N行,每行一个非负整数对Ai,Bi,表示一张牌,其中Ai表示牌的数码,Bi表示牌的花色,中间用空格隔开。特别的,我们用1来表示数码A,11表示数码J,12表示数码Q,13表示数码K;黑桃、红心、梅花、方片分别用1-4来表示;小王的表示方法为01,大王的表示方法为02。
输出
共T行,每行一个整数,表示打光第T组手牌的最少次数。
样例输入
1 8
7 4
8 4
9 1
10 4
11 1
5 1
1 4
1 1
样例输出
3
solution
一道大暴搜
开始的时候没有想到应该按照什么样的顺序搜,弄得很没有头绪
正解是想找长的搜,我是先找的各种顺子,然后是四个及带的,然后是三个及带的;最后是单牌,在搜的时候,要保证枚举到了各种情况,这样才能保证找到了最优解,所以用到了memcpy,枚举到了一种情况就继续搜索,在最后有一个极大的剪枝,当在排除掉所有枚举单牌的情况下,可以把剩下的手牌全部按相同的分次出掉,因为如果还有顺子之类的东西,在前面会枚举到,而且在最后扔的情况也不是最优解,这样就能省掉前面一段繁琐的枚举,能快很多;可以把2的编号与其他的隔开,这样无论如何也成不了顺子,省了很多事;
感悟
1、暴力搜索越早判断越好
2、在搜的时候找准一种顺序全部枚举,可以先不考虑时间问题,可以在最后在集中想剪枝;
3、memcpy超级有用,小数组都可以这么搞;
1 #include<cmath> 2 #include<queue> 3 #include<cstdio> 4 #include<cstdlib> 5 #include<cstring> 6 #include<iostream> 7 #include<algorithm> 8 using namespace std; 9 int T,n,N,ans,lim; 10 void find(int a[20],int num,int sum){ 11 if(num>ans) return ; 12 if(sum==n){ 13 ans=num-1; 14 return ; 15 } 16 int b[20],ji=1; 17 if(n-sum>=6){ 18 for(int i=1;i<=lim;i++){ 19 if(a[i]<3){ 20 ji=i+1; continue; 21 } 22 if(i-ji+1>=2){ 23 memcpy(b,a,sizeof(b)); 24 for(int j=ji;j<=i;j++){ 25 b[j]-=3; 26 } 27 find(b,num+1,sum+3*(i-ji+1)); 28 } 29 } 30 ji=1; 31 for(int i=1;i<=lim;i++){ 32 if(a[i]<2){ 33 ji=i+1; continue; 34 } 35 if(i-ji+1>=3){ 36 memcpy(b,a,sizeof(b)); 37 for(int j=ji;j<=i;j++){ 38 b[j]-=2; 39 } 40 find(b,num+1,sum+2*(i-ji+1)); 41 } 42 } 43 } 44 if(n-sum>=5){ 45 ji=1; 46 for(int i=1;i<=lim;i++){ 47 if(a[i]<1){ 48 ji=i+1; continue; 49 } 50 if(i-ji+1>=5){ 51 memcpy(b,a,sizeof(b)); 52 for(int j=ji;j<=i;j++){ 53 b[j]--; 54 } 55 find(b,num+1,sum+(i-ji+1)); 56 } 57 } 58 } 59 int c[20]; 60 if(n-sum>=4){ 61 for(int i=1;i<=lim;i++){ 62 if(a[i]==4){ 63 memcpy(b,a,sizeof(b)); 64 b[i]-=4; 65 find(b,num+1,sum+4); 66 memcpy(b,a,sizeof(b)); 67 b[i]-=4; 68 int mk, he=0; 69 if(n-sum-4>=4){ 70 for(int j=1;j<=N;j++){ 71 if(b[j]>=2){ 72 if(b[j]==4){ 73 memcpy(c,b,sizeof(c)); 74 c[j]-=4; 75 find(c,num+1,sum+8); 76 } 77 for(int k=j+1;k<=N;k++){ 78 if(b[k]>=2){ 79 memcpy(c,b,sizeof(c)); 80 c[j]-=2; c[k]-=2; 81 find(c,num+1,sum+8); 82 } 83 } 84 } 85 } 86 } 87 if(n-sum-2>=2){ 88 for(int j=1;j<=N;j++){ 89 if(b[j]>=1){ 90 if(b[j]==2){ 91 memcpy(c,b,sizeof(c)); 92 c[j]-=2; 93 find(c,num+1,sum+6); 94 } 95 for(int k=j+1;k<=N;k++){ 96 if(b[k]>=1){ 97 memcpy(c,b,sizeof(c)); 98 c[j]--; c[k]--; 99 find(c,num+1,sum+6); 100 } 101 } 102 } 103 } 104 } 105 106 } 107 } 108 } 109 if(n-sum>=3){ 110 for(int i=1;i<=lim;i++){ 111 if(a[i]>=3){ 112 memcpy(b,a,sizeof(b)); 113 b[i]-=3; 114 find(b,num+1,sum+3); 115 memcpy(b,a,sizeof(b)); 116 b[i]-=3; 117 if(n-sum-3>=2){ 118 for(int j=1;j<=N;j++){ 119 if(b[j]>=2){ 120 memcpy(c,b,sizeof(c)); 121 c[j]-=2; 122 find(c,num+1,sum+5); 123 } 124 } 125 } 126 if(n-sum-3>=1){ 127 for(int j=1;j<=N;j++){ 128 if(b[j]>=1){ 129 memcpy(c,b,sizeof(c)); 130 c[j]--; 131 find(c,num+1,sum+4); 132 } 133 } 134 } 135 } 136 } 137 } 138 int he=num-1; 139 for(int i=1;i<=lim;i++){ 140 if(a[i]) he++; 141 if(he>=ans) return ; 142 } 143 ans=min(ans,he); 144 } 145 int main(){ 146 //freopen("a.in","r",stdin); 147 //freopen("landlords.in","r",stdin); 148 //freopen("landlords.out","w",stdout); 149 scanf("%d%d",&T,&n); 150 int a[20]; 151 N=14; lim=18; 152 while(T--){ 153 memset(a,0,sizeof(a)); 154 int x,y; 155 for(int i=1;i<=n;i++){ 156 scanf("%d%d",&x,&y); 157 if(x==1) a[14]++; 158 else if( x==2 ) a[16]++; 159 else if(x==0 && y==1) a[17]++; 160 else if(x==0 && y==2) a[17]++; 161 else a[x]++; 162 } 163 ans=0x7fffffff; 164 find(a,1,0); 165 printf("%d\n",ans); 166 } 167 return 0; 168 }