刚开始读到题目的时候,非常懵逼,非常崩溃,写着写着呢,也有点崩溃,细节有点多。
这个做法呢,只能过掉官方数据,洛谷上好像有加强版,只能得$86$分,就没有管了。
大概说一下思路:
暴力搜索每一种可能的情况,如果可以就递归下去,然后回溯。
搜索框架的话,大概就是把当前搜到的出牌次数传到参数里面,如果参数已经大于了当前的最小答案就剪枝退出。然后在当前状态下枚举每一种出牌的情况,能够出就出,然后递归到下一层,参数出牌次数+1。
可以证明不会无限递归下去,因为在参数已经大于了当前的最小答案的时候就退出了。
可以分为三大类出牌:顺子、带牌、散牌
还有一些细节什么的:
1.把$A$放在$K$后面,顺子的时候比较方便
2.$2$不能在顺子里面(当时还理解了好久$2$点是什么,还以为是什么其他代称...)
3.王。之前以为王只可以单出或者火箭,没有认真读题,王是可以被带的,但是只能带单牌,王不能看成一对,他们牌值不一样。
4.后来发现顺子和带牌打完之后,可以不用单独考虑打散牌(单牌,对子,三张牌),因为如果剩下,$1$次就可以打出去一种,而且无论如何只打$1$次用的打牌次数会最少。而且散牌不用考虑与其他牌的组合。(可以感性理解,次数最多肯定是每一次都按种数打散牌)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<algorithm> 3 #include<vector> 4 #include<cstring> 5 #include<queue> 6 #include<map> 7 #include<iostream> 8 using namespace std; 9 #define ll long long 10 #define INF 0x3f3f3f3f 11 #define N 30 12 int rd() 13 { 14 int f=1,s=0;char c=getchar(); 15 while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();} 16 while(c>='0'&&c<='9'){s=(s<<3)+(s<<1)+(c^48);c=getchar();} 17 return f*s; 18 } 19 //J->11 Q->12 K->13 A->14 2->15 King->16 20 //王特殊处理 只能出火箭或者单出(糟糕 还可以单带 21 //2不能出顺子 22 int n,ans; 23 int cnt[N]; 24 void dfs(int res) 25 { 26 if(res>=ans) return ;//剪枝 提前退出 27 /* 28 //-----炸弹 29 for(int i=3;i<=15;i++) 30 if(cnt[i]>=4) 31 { 32 cnt[i]-=4; 33 dfs(res+1); 34 cnt[i]+=4; 35 } 36 //----- 37 //-----对子牌 38 for(int i=3;i<=15;i++) 39 if(cnt[i]>=2) 40 {//2个对子...就是炸弹了 应该在前面判断过了 41 cnt[i]-=2; 42 dfs(res+1); 43 cnt[i]+=2; 44 } 45 //----- 46 //-----三张牌 47 for(int i=3;i<=15;i++) 48 if(cnt[i]>=3) 49 { 50 cnt[i]-=3; 51 dfs(res+1); 52 cnt[i]+=3; 53 } 54 //----- 55 */ 56 //-----顺子 57 //---单顺子 58 int k=0; 59 for(int i=3;i<=14;i++)//不包括2点 60 { 61 if(cnt[i]==0) k=0;//顺子断了 从这里重新开始 62 else//否则就接下去 63 { 64 k++; 65 if(k>=5) 66 { 67 for(int j=i;j>=i-k+1;j--) 68 cnt[j]--; 69 dfs(res+1); 70 for(int j=i;j>=i-k+1;j--) 71 cnt[j]++; 72 } 73 } 74 } 75 //--- 76 //---双顺子 77 k=0; 78 for(int i=3;i<=14;i++)//不包括2点 79 { 80 if(cnt[i]<2) k=0;//顺子断了 从这里重新开始 81 else//否则就接下去 82 { 83 k++; 84 if(k>=3) 85 { 86 for(int j=i;j>=i-k+1;j--) 87 cnt[j]-=2; 88 dfs(res+1); 89 for(int j=i;j>=i-k+1;j--) 90 cnt[j]+=2; 91 } 92 } 93 } 94 //--- 95 //---三顺子 96 k=0; 97 for(int i=3;i<=14;i++)//不包括2点 98 { 99 if(cnt[i]<3) k=0;//顺子断了 从这里重新开始 100 else//否则就接下去 101 { 102 k++; 103 if(k>=2) 104 { 105 for(int j=i;j>=i-k+1;j--) 106 cnt[j]-=3; 107 dfs(res+1); 108 for(int j=i;j>=i-k+1;j--) 109 cnt[j]+=3; 110 } 111 } 112 } 113 //--- 114 //----- 115 //-----带牌 116 for(int i=3;i<=15;i++) 117 { 118 if(cnt[i]<3) continue; 119 //3带X 120 cnt[i]-=3; 121 for(int j=3;j<=16;j++) 122 { 123 if(i==j) continue; 124 if(cnt[j]>=1) 125 { 126 cnt[j]--; 127 dfs(res+1); 128 cnt[j]++; 129 } 130 if(cnt[j]>=2&&j!=16/*王不能一起带*/) 131 { 132 cnt[j]-=2; 133 dfs(res+1); 134 cnt[j]+=2; 135 } 136 } 137 cnt[i]+=3; 138 //4带X(两个单张或两个对) 139 if(cnt[i]<4) continue; 140 cnt[i]-=4; 141 for(int j=3;j<=16;j++) 142 { 143 if(i==j||cnt[j]<=0) continue; 144 cnt[j]--;//两张单牌 145 for(int k=3;k<=16;k++) 146 { 147 if(k==j||k==i||cnt[k]<1) continue; 148 cnt[k]--; 149 dfs(res+1); 150 cnt[k]++; 151 } 152 cnt[j]++; 153 if(cnt[j]<2||j==16/*王不能一起*/) continue; 154 cnt[j]-=2;//两对对子 155 for(int k=3;k<=15;k++) 156 { 157 if(k==j||k==i||cnt[k]<2) continue; 158 cnt[k]-=2; 159 dfs(res+1); 160 cnt[k]+=2; 161 } 162 cnt[j]+=2; 163 } 164 cnt[i]+=4; 165 } 166 //----- 167 for(int i=3;i<=16;i++) 168 if(cnt[i]) res++; 169 //突然发现 单张 对子三张都不用特殊考虑 反正能一次出完剩下的所有牌 170 ans=min(ans,res); 171 } 172 int main() 173 { 174 int T=rd(),n=rd(); 175 while(T--) 176 { 177 memset(cnt,0,sizeof(cnt)); 178 ans=INF; 179 for(int i=1;i<=n;i++) 180 { 181 int m=rd(),opt=rd(); 182 if(m==0) m=16; 183 if(m==1) m=14; 184 if(m==2) m=15; 185 cnt[m]++; 186 } 187 dfs(0); 188 printf("%d ",ans); 189 } 190 return 0; 191 }