【bzoj4325】NOIP2015 斗地主(&“加强”版) 搜索
题目描述
牛牛最近迷上了一种叫斗地主的扑克游戏。斗地主是一种使用黑桃、红心、梅花、方片的A到K加上大小王的共54张牌来进行的扑克牌游戏。在斗地主中,牌的大小关系根据牌的数码表示如下:3<4<5<6<7<8<9<10<J<Q<K<A<2<小王<大王,而花色并不对牌的大小产生影响。每一局游戏中,一副手牌由n张牌组成。游戏者每次可以根据规定的牌型进行出牌,首先打光自己的手牌一方取得游戏的胜利。现在,牛牛只想知道,对于自己的若干组手牌,分别最少需要多少次出牌可以将它们打光。请你帮他解决这个问题。需要注意的是,本题中游戏者每次可以出手的牌型与一般的斗地主相似而略有不同。具体规则如下:
输入
第一行包含用空格隔开的2个正整数T,N,表示手牌的组数以及每组手牌的张数。
输出
共T行,每行一个整数,表示打光第T组手牌的最少次数。
样例输入
1 8
7 4
8 4
9 1
10 4
11 1
5 1
1 4
1 1
样例输出
3
题解
搜索
15年的一道神题,然而由于数据太水而各种水过
后来UOJ又出了个“加强”版,可以构造数据,难度远远大于原题
先说一下大体思路吧:就是各种爆搜。
这里爆搜时有些技巧:
1.当不打龙的时候可以直接计算出最优方案,不需要继续搜索。
2.由于打牌的顺序对答案是没有影响的,因此可以先爆搜打出去牌数多的牌型,即先打多龙再打单龙,先打大龙再打小龙。
然后原题就可以直接水过了。。。
下面说“加强”版:
-1.四张的可以拆成两个两张出
-2.四张的可以拆成一个三张一个一张出
-3.三张的可以拆成一个两张一个一张出
-4.两张的可以拆成两个一张出
-5.“火箭”不算对牌
所以在计算不打龙情况下要出多少次时,还需要枚举前4种情况。。。于是写了8个dfs。。。
然后就能水过这两道题了,代码巨丑= =
做完这两道题以后再也不会玩斗地主了。。。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; int a[20] , ans , n , now , cnt[5]; void dfs8() { int c1 = cnt[1] , c2 = cnt[2] , c3 = cnt[3] , c4 = cnt[4] , c = 0; while(c4) { c ++ , c4 -- ; if(c2 >= 2) c2 -= 2; else if(c1 >= 2) c1 -= 2; } while(c3) { c ++ , c3 -- ; if(c2) c2 -- ; else if(c1) c1 -- ; } if(a[15] && a[16] && c1 >= 2) c -- ; ans = min(ans , now + c + c1 + c2); } void dfs7() { int i; for(i = 0 ; i <= cnt[2] ; i ++ ) { cnt[2] -= i , cnt[1] += i << 1; dfs8(); cnt[2] += i , cnt[1] -= i << 1; } } void dfs6() { int i; for(i = 0 ; i <= cnt[3] ; i ++ ) { cnt[3] -= i , cnt[2] += i , cnt[1] += i; dfs7(); cnt[3] += i , cnt[2] -= i , cnt[1] -= i; } } void dfs5() { int i; for(i = 0 ; i <= cnt[4] ; i ++ ) { cnt[4] -= i , cnt[3] += i , cnt[1] += i; dfs6(); cnt[4] += i , cnt[3] -= i , cnt[1] -= i; } } void dfs4() { int i; memset(cnt , 0 , sizeof(cnt)); for(i = 2 ; i <= 16 ; i ++ ) cnt[a[i]] ++ ; for(i = 0 ; i <= cnt[4] ; i ++ ) { cnt[4] -= i , cnt[2] += i << 1; dfs5(); cnt[4] += i , cnt[2] -= i << 1; } } void dfs3(int p , int l) { if(now >= ans) return; int i , j , k; for(i = min(n , l) ; i >= 5 ; i -- ) { for(j = (i == l ? p : 3) ; j <= 15 - i ; j ++ ) { for(k = j ; k < j + i ; k ++ ) if(!a[k]) break; if(k == j + i) { for(k = j ; k < j + i ; k ++ ) a[k] -- , n -- ; now ++ , dfs3(j , i) , now -- ; for(k = j ; k < j + i ; k ++ ) a[k] ++ , n ++ ; } } } dfs4(); } void dfs2(int p , int l) { if(now >= ans) return; int i , j , k; for(i = min(n / 2 , l) ; i >= 3 ; i -- ) { for(j = (i == l ? p : 3) ; j <= 15 - i ; j ++ ) { for(k = j ; k < j + i ; k ++ ) if(a[k] < 2) break; if(k == j + i) { for(k = j ; k < j + i ; k ++ ) a[k] -= 2 , n -= 2; now ++ , dfs2(j , i) , now -- ; for(k = j ; k < j + i ; k ++ ) a[k] += 2 , n += 2; } } } dfs3(-1 , 100); } void dfs1(int p , int l) { if(now >= ans) return; int i , j , k; for(i = min(n / 3 , l) ; i >= 2 ; i -- ) { for(j = (i == l ? p : 3) ; j <= 15 - i ; j ++ ) { for(k = j ; k < j + i ; k ++ ) if(a[k] < 3) break; if(k == j + i) { for(k = j ; k < j + i ; k ++ ) a[k] -= 3 , n -= 3; now ++ , dfs1(j + i , i) , now -- ; for(k = j ; k < j + i ; k ++ ) a[k] += 3 , n += 3; } } } dfs2(-1 , 100); } int main() { int T , k; scanf("%d%d" , &T , &k); while(T -- ) { int i , v , w; n = k; memset(a , 0 , sizeof(a)); for(i = 1 ; i <= n ; i ++ ) { scanf("%d%d" , &v , &w); if(!v) a[w + 14] ++ ; else if(v == 1) a[14] ++ ; else a[v] ++ ; } ans = 1 << 30 , now = 0 , dfs1(-1 , 100); printf("%d\n" , ans); } return 0; }