不得不承认,算法搁置了一些时间,代码的风格下降了好多! 贴上一个曹点多多且丑的代码! Orz...
题目要求:
编码:3表示3点 ,4表示4点点 。。。。 10表示10点 11表示J 12表示Q 13表示K 14表示A 15表示2 16表示小王 17表示大王
要求:
1)出牌牌型包括(单子,对子, 三张,三带一,三带二,顺子,连对(3连对,4连对,5连对....),四张炸弹,四带一 ,四带二(四带2个单张,四带2个对子),对王炸弹)
2)初始化1副牌(54张),随机发一手牌(17张)显示出来
3)手动输入一个几张牌。判断输入牌是否是1)中的牌型。 程序根据牌型判断自己的手牌是否可以压该牌,如果可以压,则显示可以压的牌型,并输出,从手牌减去已压的牌。显示剩余的牌
4)循环过程要求3 直到牌出完。
代码:
1 #define Gxjun
2 #define LOCAL
3 #define Test
4 #include<stdio.h>
5 #include<string.h>
6 #include<stdlib.h>
7 #include<time.h>
8 #define Arr_Len(arr) sizeof(arr)/sizeof(arr[0])
9 #define maxn 18
10
11
12 int cod[maxn]={0};
13 int mycod[maxn];
14 int chu[maxn];
15 int rtmp[maxn]={0} ,mtmp[maxn]={0};
16 int mx , mn ;
17
18 typedef struct LianD
19 {
20 int en ; //连对结尾的牌号
21 int du ; //最接近的度数
22 int cnt ; //连对的个数
23 int tem ; //临时存储的结尾牌号
24 //复制值函数
25 void copfun(struct LianD *Ld){
26
27 Ld->cnt=cnt;
28 Ld->du=du;
29 Ld->en=en;
30 Ld->tem=tem;
31 }
32 //初始化
33 void init(){
34 en=du=cnt=tem=0;
35 }
36 };
37
38 enum AlgTyNn{ ShunZi=1 ,
39 LianDui
40 };
41
42 int cmp(const void * arg , const void * brg){
43
44 int * ar =(int *) arg ;
45 int * br =(int *) brg ;
46 return *ar - *br ;
47
48 }
49
50 #ifdef Test
51 void init()
52 {
53 int i , cnt=0 ;
54
55
56 #ifdef Test
57 #ifndef LOCAL
58 #define LOCAL
59 #endif // LOCAL
60 #endif // Test
61
62 #ifdef LOCAL
63 freopen("data.in","r",stdin);
64 #endif // Test
65 memset(mycod , 0 , sizeof(mycod));
66 mycod[cnt]=17;
67 cod[17]=cod[16]=1; //一对王
68 for(i=3 ; i<16 ;i++)
69 cod[i]=4;
70 while(++cnt<maxn){
71 scanf("%d" ,&mycod[cnt]);
72 cod[mycod[cnt]]--;
73 }
74 #ifdef Gxjun
75 fclose(stdin);
76 freopen("CON","r",stdin);
77 #endif // Gxjun
78 qsort(mycod+1 , cnt-1 , sizeof(mycod-1) , cmp);
79 }
80
81 #endif // Test
82
83 #ifndef Test
84 void init(){
85
86 int i , hopg ,cnt=0 ;
87 memset( mycod , 0 , sizeof(mycod) ) ;
88 mycod[cnt]=17;
89 for(i=3 ; i<16 ;i++)
90 cod[i]=4;
91 cod[16]=1; //一对王
92 cod[17]=1;
93 srand(time(NULL));
94
95 while(cnt<maxn){
96
97 hopg = rand()%maxn ;
98
99 if( hopg>0&&cod[hopg] > 0 ){
100 cod[hopg]--;
101 mycod[++cnt]=hopg;
102 }
103 }
104 qsort(mycod+1 , cnt-1 , sizeof(mycod-1) , cmp);
105
106 }
107 #endif // Test
108
109 //对方输出
110 void inputs(){
111
112 int cnt=0;
113 memset(chu , 0 , sizeof(chu));
114 while(1){
115 scanf("%d",&chu[++cnt]);
116 if(chu[cnt]<0) break;
117 }
118 chu[0]=cnt-1 ; //最开头放置纸牌的数目
119
120 }
121
122 //打印剩余
123 void output(){
124
125 printf(" | 剩牌系: ");
126 //剩余牌系
127 int cont=0;
128 for(int j=3 ; j<maxn ;j++){
129 cont+=mtmp[j];
130 for(int k=0; k<mtmp[j] ; k++){
131 printf("%d ",j);
132 }
133 }
134 mtmp[0]=cont;
135 printf("\t剩牌数: [ %d ]",mtmp[0]);
136 puts("");
137 }
138
139 //计数排序
140 void connt(int *tmp , const int ss []){
141
142 //memset(tmp ,0 ,sizeof(tmp));
143 tmp[0]=ss[0];
144 for(int i= 1 ; i<=ss[0] ; i++)
145 tmp[ss[i]]++; //计数排序
146 }
147
148 //判断是否有炸弹或者王炸
149 void ZhaDan(){
150
151 for(int i=3 ; i<maxn ;i++)
152 if(mtmp[i]==4){
153 printf("%d %d %d %d ",i,i,i,i);
154 mtmp[i]-=4;
155 output();
156 return ;
157 }
158
159 if(mtmp[16]==1&&mtmp[17]==1){
160 printf("%d %d ",16,17);
161 mtmp[16]=mtmp[17]=0;
162 output();
163 return ;
164 }
165 puts("没有牌能压过!");
166 return ;
167 }
168
169
170 //如果是连对处理方案
171 void AlgLDOrShZi(int var){
172
173 int i=mn+1 ,len = mx-mn+1 ,kk=0;
174 struct LianD Ld , Tmpld;
175
176 Ld.init();
177 //时间复杂度为0(n^2)
178 for( i = mn+1 ; i<15 ; i++){
179 Tmpld.init();
180 Tmpld.tem=i;
181 for(int j=i ; j<i+len ; j++){
182 if(mtmp[j]>=var){
183
184 if(mtmp[j]==var){
185 Tmpld.du++;
186 }
187 Tmpld.cnt++;
188 }else{
189 Tmpld.init();
190 Tmpld.tem=j+1;
191 }
192 if(Tmpld.cnt==len){
193 Tmpld.en =Tmpld.tem;
194 if(Ld.en==0||Ld.du<Tmpld.du)
195 Tmpld.copfun(&Ld);
196 break;
197 }
198 }
199 }
200 if(Ld.cnt == len){
201 puts("提示:");
202 for(i=Ld.en; i<Ld.en+len ;i++){
203 mtmp[i]-=var;
204 kk=0;
205 while(kk++<var)
206 printf("%d " ,i);
207 }
208 output();
209 }else{
210 //寻求炸弹或者王炸
211 ZhaDan();
212 }
213
214 }
215
216 //三带的处理情况
217
218 void AlgShanDai(){
219
220 int pos=0 , cnt=0 , res=0,fcnt=0;
221 int mcnt=0 , mpos=0 ;
222 //分析牌型
223 for(int i=mn;i<=mx ;i++){
224 if(rtmp[i]==3){
225 pos=i;
226 cnt++;
227 }else if(rtmp[i]>0){
228 res+=rtmp[i]; //统计剩余牌的数量
229 fcnt++;
230 }
231 }
232 if((fcnt==cnt&&res%cnt==0)||(fcnt<cnt&&(fcnt*res==cnt||0==res))) ;
233 else{
234 puts("输出的牌不符合规则!请重新输出:");
235 return ;
236 }
237 res/=cnt;
238 for(int i=pos-cnt+2 ; i<=pos; i++){
239 if(rtmp[i]!=3){
240 puts("输出的牌不符合规则!请重新输出:");
241 return ;
242 }
243 }
244
245 //如果为三带情况 即 cnt =1
246 for(int i=pos-cnt+2 ; i<17 ;i++){
247 if(mtmp[i]==3){
248 mpos=i;
249 mcnt++;
250 }else
251 mcnt=0;
252 if(mcnt==cnt) break;
253 }
254 //查询副牌是否能够满足
255 if(mcnt==cnt){
256 //说明有解决方案
257 int stpos = mpos - cnt +1 ;
258 int src[maxn]={0} ,tt=0;
259 bool tag = false;
260 for(int i=3 ; i<17 ;i++){
261 //满足不再连续范围之内的即可333444不能为3,4
262 if(i<stpos||i>mpos){
263 if(mtmp[i]>=res){
264 for(int kk=0 ; kk<mtmp[i] ;kk+=res){
265 src[tt++]=i;
266 if(tt==cnt){
267 tag = true;
268 break;
269 }
270 }
271 }
272 }
273 if(tag) break ;
274 }
275 if(tt==cnt){
276 //则解决方案为
277 int mstpos = mpos - cnt +1;
278 for(int i=mstpos ; i<=mpos ; i++){
279 printf("%d %d %d " ,i,i,i);
280 mtmp[i]-=3;
281 }
282 //打印副牌
283 for(int i=0; i<tt ;i++){
284 for(int k=0 ;k<res ;k++){
285 printf("%d ",src[i]);
286 }
287 mtmp[src[i]]-=res;
288 }
289 output(); //打印剩余牌
290 }
291 }else{
292
293 //查询是否有炸弹
294 ZhaDan();
295 }
296 }
297
298 //四带情况
299 void AlgSiDai(){
300 if(chu[0]>4) ZhaDan();
301 else{
302 for(int i=chu[1]+1 ; i<15 ; i++){
303 if(mtmp[i]==4){
304 printf("%d %d %d %d ",i,i,i,i);
305 mtmp[i]-=4;
306 output();
307 return ;
308 }
309 }
310 if(mtmp[16]==1&&mtmp[17]==1){
311 printf("%d %d ",16,17);
312 mtmp[16]=mtmp[17]=0;
313 output();
314 return ;
315
316
317
318
319
320 }
321 puts("没有牌能压过!");
322 return ;
323 }
324 }
325
326
327 //对子的情况
328 void AlgDuiZi(){
329 for(int i=chu[1]+1 ; i<16 ;i++)
330 {
331 if(mtmp[i]>1&&mtmp[i]<4){
332 printf("%d %d \n",i,i);
333 mtmp[i]-=2;
334 output();
335 return ;
336 }
337 }
338 ZhaDan();
339 }
340
341 //对于个子的情况
342 void AlgGreZi(){
343
344 for(int i=chu[1]+1 ; i<18 ;i++)
345 {
346 if(mtmp[i]>0&&mtmp[i]<4){
347 printf("%d \n",i);
348 mtmp[i]-=1;
349 output();
350 return ;
351 }
352 }
353 ZhaDan();
354 }
355
356 //查询对应的方案
357
358 //对子
359
360 bool IsDuiZi(){
361
362 if(chu[0]==2) //则必定是对子
363 return true;
364 return false ;
365 }
366
367 //个子
368 bool IsGreZi(){
369
370 if(chu[0]==1) //则必定是对子
371 return true;
372
373 return false ;
374 }
375
376 //判断是否是顺子
377 bool IsShunZi(){
378
379 //顺子的条件
380 if(chu[0]>4){
381 if((mx-mn+1==chu[0])&& mx<15)
382 return true ;
383 }
384 return false;
385 }
386
387 //判断是否是连对
388 bool IsLianDui(){
389 if(chu[0]>5&&mx<15){
390 for(int i=mn; i<=mx ;i++)
391 if(rtmp[i]!=LianDui)
392 return false;
393 return true ;
394 }
395 return false;
396 }
397
398 //判断是否是三带
399 bool IsShanDai(){
400
401 for(int i=mn; i<=mx ;i++)
402 if(rtmp[i]==3)
403 return true;
404
405 return false ;
406 }
407
408
409
410 //判断是否是四带或者炸弹
411 bool IsSiDai(){
412
413 for(int i=mn; i<=mx ;i++)
414 if(rtmp[i]==4)
415 return true;
416
417 return false ;
418 }
419
420
421 //统计判断
422 void AlgMxn(){
423
424 //求最大值,最小值
425 for(int i =3 ; i<= 17 ; i++)
426 if(rtmp[i]>0){
427 mn = i;
428 break;
429 }
430 for(int i =17 ; i>= 3 ; i--)
431 if(rtmp[i]>0){
432 mx = i;
433 break;
434 }
435
436 }
437
438 void print(){
439
440 int i;
441 for(i=1; i<17; i++)
442 printf("%d ", mycod[i]);
443 printf("%d\n", mycod[17]);
444
445 }
446
447 //检测出牌方
448 bool checked(){
449
450 for(int i=1 ; i<maxn ;i++){
451 if(cod[i]<rtmp[i])
452 return false;
453 }
454
455 if(IsGreZi()
456 ||IsDuiZi()
457 ||IsShunZi()
458 ||IsSiDai()
459 ||IsShanDai()){
460 for(int i=1; i<=chu[0] ;i++){
461 cod[chu[i]]--;
462 }
463 }
464 return true ;
465 //如果为一对王
466 if(chu[0]==2&&mtmp[16]==1&&mtmp[17]==1)
467 return true;
468 return false;
469 }
470
471 int main(int argc , char * argv)
472 {
473
474 init();
475
476 memset(mtmp , 0 , sizeof(mtmp));
477
478 connt(mtmp , mycod);
479 print();
480
481 while(true){
482 printf("请出牌:\n");
483 while(1){
484 inputs();
485 memset(rtmp , 0 ,sizeof(rtmp));
486 connt(rtmp , chu);
487 AlgMxn();
488 if(checked()) break;
489 else
490 puts("输出的牌不符合规则!请重新输出:");
491 }
492 //如果满足顺子
493 if(IsGreZi())
494 AlgGreZi();
495 else
496 if(IsDuiZi())
497 AlgDuiZi();
498 else
499 if(IsShunZi())
500 AlgLDOrShZi(ShunZi);
501 else
502 if(IsLianDui())
503 AlgLDOrShZi(LianDui); //对于连对的情况
504 else
505 if(IsShanDai())
506 AlgShanDai();
507 else
508 if(IsSiDai())
509 AlgSiDai();
510
511 if(mtmp[0]<1){
512 puts("恭喜你,win!");
513 break;
514 };
515 }
516 return 0;
517
518 }