生活随笔
收集整理的這篇文章主要介紹了
斗地主(深搜+贪心+剪枝)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
斗地主
題目鏈接http://www.lydsy.com/JudgeOnline/problem.php?id=4325
solution
由于牌數不是很多,n<=23,一開始想到用狀態壓縮的方法標記每一個狀態,然后寬搜。這似乎可行,但是要找四帶二,常數非常大,代碼量也很變態,根本拿不了什么分。除此之外,沒有別的辦法來完成標記,所以寬搜不可行。那就只能深搜剪枝了。
將每種點數的牌有幾張作為參數,這是很容易想到了,然后出牌的順序顯然是不會影響結果的。在那么多的牌型中順子比較特別,只有順子是與點數大小有關的,而且一個順子的牌張數是很多的,是一種比較理想的出牌方式,所以先dfs考慮順子怎么出。對于剩下的牌,就不出順子了。因為要次數最少,一個貪心思想非常明顯——每次出的牌盡可能的多。所以先考慮四帶二對,然后四帶二張,四代一對,三帶二,三帶一……最后是炸彈,三張牌,對子(火箭),單牌。由于順序不影響結果,所以這個貪心沒有問題。在這樣的搜索方法下,再加上最優性剪枝,就可以ac了
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<cstring>
using namespace std;
int T,n,a[
20],ans;
int work(
int *
x){int y[
10],num=
0;memset(y,0,
sizeof(y));for(
int i=
0;i<=
13;i++
)y[x[i]]++
;while(y[
4]>=
1&&y[
2]>=
2){num++
;y[4]--
;y[2]-=
2;}while(y[
4]>=
1&&y[
1]>=
2){num++
;y[4]--
;y[1]-=
2;}while(y[
4]>=
1&&y[
2]>=
1){num++
;y[4]--
;y[2]--
;}while(y[
3]>=
1&&y[
2]>=
1){num++
;y[3]--
;y[2]--
;}while(y[
3]>=
1&&y[
1]>=
1){num++
;y[3]--
;y[1]--
;}return num+y[
1]+y[
2]+y[
3]+y[
4];
}
void dfs(
int *x,
int step){int yu=
work(x);ans=min(ans,step+
yu);if(step>=
ans)return;int y[
20];memset(y,0,
sizeof(y));for(
int i=
0;i<=
13;i++
)y[i]=
x[i];for(
int i=
2;i<=
13;i++
){int j=
i;while(x[j]>=
3)j++
;while(j-i>=
2){for(
int k=i;k<j;k++
)y[k]-=
3;dfs(y,step+
1);for(
int k=i;k<j;k++
)y[k]+=
3;j--
;}}for(
int i=
2;i<=
13;i++
){int j=
i;while(x[j]>=
2)j++
;while(j-i>=
3){for(
int k=i;k<j;k++
)y[k]-=
2;dfs(y,step+
1);for(
int k=i;k<j;k++
)y[k]+=
2;j--
;}}for(
int i=
2;i<=
13;i++
){int j=
i;while(x[j]>=
1)j++
;while(j-i>=
5){for(
int k=i;k<j;k++
)y[k]--
;dfs(y,step+
1);for(
int k=i;k<j;k++
)y[k]++
;j--
;}}
}
int main(){scanf("%d%d",&T,&
n);while(T--
){memset(a,0,
sizeof(a));for(
int i=
1;i<=n;i++
){int x,y;scanf("%d%d",&x,&
y);if(x==
1) x=
13;else if(x)x--
;a[x]++
;}ans=
1000000000;dfs(a,0);printf("%d\n",ans);}return 0;
} ?
轉載于:https://www.cnblogs.com/RetardedZY/p/7469878.html
總結
以上是生活随笔為你收集整理的斗地主(深搜+贪心+剪枝)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。