1. 程式人生 > 實用技巧 >P2668 鬥地主 (NOIP 提高 2015)

P2668 鬥地主 (NOIP 提高 2015)

P2668

首先,如上圖,我們有 三帶一,三帶二,單順子,雙順子,三順子和四帶二 這幾種特殊的牌型,在上圖的牌型中,火箭也是比較特殊的,但是本題中只是需要兩張鬼王牌即可,所以我們可以把火箭看為對子牌這種普通牌型

所以,我們只要在搜尋的時候對於上面的幾種特殊牌型進行特別的處理,最後剩下的手牌中每種相同點數的牌一定可以以單張牌,對子牌,三張牌和炸彈牌幾種牌型打出

除此之外,還要注意每種牌型對於牌的特殊限制(如順子中不能有 "2" 和鬼王),相應的對列舉的牌進行調整


原始碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<math.h>
#include<algorithm>
#define ll long long
using namespace std;

const ll maxn=50;
ll T,n,ans,num,c;
ll vis[maxn];

inline void dfs(ll x)
{
	if(x>=ans) return ;//最優性剪枝
	
	ll len=0;
	
	for(int i=3;i<=14;i++)//單順子
	{
		if(vis[i]==0) len=0;
		else
		{
			len++;
			if(len>=5)
			{
				for(int j=i;j>=i-len+1;j--) vis[j]--;
				dfs(x+1);
				for(int j=i;j>=i-len+1;j--) vis[j]++; 
			}
		}
	}
	
	len=0;
	
	for(int i=3;i<=14;i++)//雙順子
	{
		if(vis[i]<=1) len=0;
		else
		{
			len++;
			if(len>=3)
			{
				for(int j=i;j>=i-len+1;j--) vis[j]-=2;
				dfs(x+1);
				for(int j=i;j>=i-len+1;j--) vis[j]+=2;
			}
		}
	}
	
	len=0;
	
	for(int i=3;i<=14;i++)//三順子
	{
		if(vis[i]<=2) len=0;
		else
		{
			len++;
			if(len>=2)
			{
				for(int j=i;j>=i-len+1;j--) vis[j]-=3;
				dfs(x+1);
				for(int j=i;j>=i-len+1;j--) vis[j]+=3;
			}
		}
	}
	
	for(int i=2;i<=14;i++)
	{
		if(vis[i]==3)
		{
			vis[i]-=3;
			for(int j=2;j<=15;j++)//三帶一
			{
				if(vis[j]>=1&&j!=i)
				{
					vis[j]--;
					dfs(x+1);
					vis[j]++;
				}
			}
			for(int j=2;j<=14;j++)//三帶二
			{
				if(vis[j]>=2&&j!=i)
				{
					vis[j]-=2;
					dfs(x+1);
					vis[j]+=2;
				}
			}
			vis[i]+=3;
		}
		else if(vis[i]==4)//有四張牌可以分為出三張留一張和全部都出
		{
			vis[i]-=3;
			
			for(int j=2;j<=15;j++)//三帶一
			{
				if(vis[j]>=1&&j!=i)
				{
					vis[j]-=1;
					dfs(x+1);
					vis[j]+=1;
				}
			}
			
			for(int j=2;j<=14;j++)//三帶二
			{
				if(vis[j]>=2&&j!=i)
				{
					vis[j]-=2;
					dfs(x+1);
					vis[j]+=2;
				}
			}
			
			vis[i]+=3;
			
			vis[i]-=4;
			
			for(int j=2;j<=15;j++)
			{
				if(vis[j]>=1&&j!=i)//四帶兩張
				{
					vis[j]--;
					
					for(int k=2;k<=15;k++)
					{
						if(vis[k]>=1&&j!=k)
						{
							vis[k]--;
							dfs(x+1);
							vis[k]++;
						}
					}
					
					vis[j]++;
				}
			}
			
			for(int j=2;j<=14;j++)//四帶兩對
			{
				if(vis[j]>=2&&j!=i)
				{
					vis[j]-=2;
					
					for(int k=2;k<=14;k++)
					{
						if(vis[k]>=2&&k!=j)
						{
							vis[k]-=2;
							dfs(x+1);
							vis[k]+=2;
						}
					}
					vis[j]+=2;
				}
			}
			
			vis[i]+=4;
		}
	}
	
	for(int i=2;i<=15;i++)//全部出完
	{
		if(vis[i]) x++;
	}
	
	ans=min(ans,x);
}

int main(void)
{
//	freopen("P2668_3.in","r",stdin);
//	freopen("z.out","w",stdout);
	
	scanf("%lld%lld",&T,&n);
	
	while(T--)
	{
		memset(vis,0,sizeof(vis));
		
		ans=9999999999;
	
		for(int i=1;i<=n;i++)//把牌按照大小以 3 為基準排好
		{
			scanf("%lld%lld",&num,&c);
			if(num==1) num=14;
			if(num==0) num=15;
			vis[num]++;
		}
		
		dfs(0);
		
//		if(ans==2) ans=3;
		
		printf("%lld\n",ans);
	}
	
	return 0;
}