ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[NOIP2015提高组]斗地主(贪心+搜索)

2021-08-18 22:31:07  阅读:158  来源: 互联网

标签:NOIP2015 斗地主 ++ 13 int 三带 顺子 贪心


15 年的 NOIP 提高组试题,被搬到今天校模拟赛,只糊了 20 分,结果人均 50 分贪心……

这张图片可以作为题目描述:

image

并不是跟别人斗地主,而是要求尽快出完牌,输出步数。 \(n \le 20\),多组数据。

对于 \(n \le 5\) 的那些测试点,当然可以手工模拟。更进一步,根据打牌的经验(显然我就没有),不难产生一些贪心思路,例如先出顺子、尽可能四带二、三带二、三带一等

稍加考虑,一般的贪心都是错误的——只能采取搜索了。然而搜索的时间复杂度很高,O(TLE),所以需要剪枝

  • 贪心地搜索。每一次出牌是一个阶段,分为顺子、双顺子、三顺子、四带二、三带二、三带一 6 种出牌方式,向下递归,回溯;

  • 最优化剪枝。如果当前出牌次数大于等于当前答案,直接返回;

  • 不在“一个三、一个五”这样的零散牌上费时间,在函数体最后扫一遍剩下的牌,步数++即可。

代码实现上有很多细节,例如大小王的判断(出对子时永远 13 种牌,单出才有 14 种)或 A 的判断(它可以作为对子或顺子的一部分)。下附 AC 代码。

#include<bits/stdc++.h>
using namespace std;
inline int rd(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-') f^=1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f?x:-x;
}
int T,n,ans,a[30];
void dfs(int d){
	if(d>=ans) return;
	//1. 顺子(5)
	for(int l=3;l<=10;++l){
		if(!a[l]) continue;
		int r=l+1;
		while(r<=13&&a[r]) ++r;--r;
		if(r==13&&a[1]) ++r;
		//最长[l,r]
		if(r-l+1<5) continue;
		for(int i=l+4;i<=r;++i){
			for(int j=l;j<=i;++j)
				if(j<=13) --a[j];
				else --a[1];
			dfs(d+1);
			for(int j=l;j<=i;++j) 
				if(j<=13) ++a[j];
				else ++a[1];
		}
	} 
	//2. 双顺子(3)
	for(int l=3;l<=12;++l){
		if(a[l]<2) continue;
		int r=l+1;
		while(r<=13&&a[r]>=2) ++r;--r;
		if(r==13&&a[1]>=2) ++r;
		if(r-l+1<3) continue;
		for(int i=l+2;i<=r;++i){
			for(int j=l;j<=i;++j)
				if(j<=13) a[j]-=2;
				else a[1]-=2;
			dfs(d+1);
			for(int j=l;j<=i;++j)
				if(j<=13) a[j]+=2;
				else a[1]+=2;
		}
	} 
	//3. 三顺子(2)
	for(int l=3;l<=13;++l){
		if(a[l]<3) continue;
		int r=l+1;
		while(r<=13&&a[r]>=3) ++r;--r;
		if(r==13&&a[1]>=3) ++r;
		if(r-l+1<2) continue;
		for(int i=l+1;i<=r;++i){
			for(int j=l;j<=i;++j)
				if(j<=13) a[j]-=3;
				else a[1]-=3;
			dfs(d+1);
			for(int j=l;j<=i;++j)
				if(j<=13) a[j]+=3;
				else a[1]+=3;
		}
	}
	//4. 四带二 
	for(int i=1;i<=13;++i){
		if(a[i]<4) continue;
		a[i]=0;
		//4.1 带两张零牌 / 一个对子  
		for(int j=1;j<=14;++j){
			if(!a[j]) continue;
			--a[j];
			for(int k=1;k<=14;++k){
				if(!a[k]) continue;
				--a[k],dfs(d+1),++a[k];
			}
			++a[j];
		}
		//4.2 带两个对子 
		for(int j=1;j<=13;++j){
			if(a[j]<2) continue;
			a[j]-=2;
			for(int k=1;k<=13;++k){
				if(a[k]<2) continue;
				a[k]-=2,dfs(d+1),a[k]+=2;
			}
			a[j]+=2;
		} 
		a[i]=4;
	} 
	//5. 三带二 / 三带一 
	for(int i=1;i<=13;++i){
		if(a[i]<3) continue;
		a[i]-=3;
		for(int j=1;j<=13;++j){
			if(a[j]<2||i==j) continue;
			a[j]-=2,dfs(d+1),a[j]+=2;
		}
		for(int j=1;j<=14;++j){
			if(!a[j]||i==j) continue;
			--a[j],dfs(d+1),++a[j];
		}
		a[i]+=3;
	} 
	//6. 零牌 
	for(int i=1;i<=14;++i)
		if(a[i]) ++d;
	ans=min(ans,d);
}
int main(){
	T=rd(),n=rd();
	while(T--){
		ans=0x7fffffff;
		memset(a,0,sizeof(a));
		for(int i=1,x,y;i<=n;++i){
			x=rd(),y=rd();
			x?++a[x]:++a[14];
		}
		dfs(0);
		printf("%d\n",ans);
	}
	return 0;
}

THE END

标签:NOIP2015,斗地主,++,13,int,三带,顺子,贪心
来源: https://www.cnblogs.com/q0000000/p/15159184.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有