Luogu P2668 斗地主(NOIP2015)

本文作者MiserWeyte

还记得那道我只用特判得了30分的“斗地主”吗?

我今天脑抽打算把它改A掉。为什么不用这大好时光去干些更有意义的事

于是我就挖了这个坑。

题解:

题目链接:P2668 斗地主

本题就是一道大搜索。按照顺序,先搜顺子、双顺子、三顺子;再搜三张牌、四张牌。
注意顺子的回溯部分,以及有四张同点数牌的时候也可以只出三张。
基本思路除了搜索之外就是贪心,优先将搜出来的多张牌一起出。
我是将双王分开存储的,防止三/四带对的时候将双王带上。
最后结束出单牌的时候,记得将对子和双王一起出。
这段代码过不了加强版数据,需要将贪心的思想换成dp。
源码有注释。

//MiserWeyte is now "mzWyt"
#include <bits/stdc++.h>
using namespace std;
int T, n, num, ul, ans;
int card[20]; // 存储每种牌张数 

void power_sol(){ // 暴力解决n<=4 
	if(n==2){
		int c1, c2;
		while(T--){
			scanf("%d%d%d%d", &c1, &ul, &c2, &ul);
			if(c1==c2) printf("1\n");
			else printf("2\n");
		}
		return;
	}
	if(n==3){
		int c1, c2, c3;
		while(T--){
			scanf("%d%d%d%d%d%d", &c1, &ul, &c2, &ul, &c3, &ul);
			if(c1==c2 && c2==c3) printf("1\n");
			else if(c1 == c2 && c2 != c3) printf("2\n");
			else if(c1 != c2 && c2 == c3) printf("2\n");
			else if(c1 == c3 && c2 != c3) printf("2\n");
			else printf("3\n");
		}
		return;
	}
	if(n==4){
		int c1, c2, c3, c4;
		while(T--){
			scanf("%d%d%d%d%d%d%d%d", &c1, &ul, &c2, &ul, &c3, &ul, &c4, &ul);
			if(c1==c2&&c2==c3&&c3==c4) printf("1\n");
			else if(c1==c2&&c2==c3&&c3!=c4) printf("1\n");
			else if(c1==c2&&c2!=c3&&c2==c4) printf("1\n");
			else if(c1!=c2&&c2==c3&&c3==c4) printf("1\n");
			else if(c1==c2&&c3==c4) printf("2\n");
			else if(c1==c3&&c2==c4) printf("2\n");
			else if(c1==c4&&c2==c3) printf("2\n");
			else if(c1==c2||c1==c3||c1==c4||c2==c3||c2==c4||c3==c4) printf("3\n");
			else printf("4\n");			
		}
		return;
	}
}

void init(){ 
	memset(card, 0, sizeof(card));	// 初始化清空card数组 
	ans = n; // 初始化ans为n(不存在比全出单张更劣的方案) 

}

void dbg(){ // 调试,输出当前所有牌 
	cout << endl;
	for(int i=0; i<=14; i++){
		cout << i << '\t' << card[i] << '\n';
	}
	cout << endl;
}

void dfs(int dep){
	if(dep > ans) return; // 剪枝  
	int l; // 当前顺子长度 
	l = 0;
	for(int i=3; i<=14; i++){ // 搜索单顺子 
		if(card[i]) l ++;
		else l = 0;
		if(l >= 5){ // 长度达到顺子 
			for(int j=i-l+1; j<=i; j++)	card[j] --;	
//			dbg();
			dfs(dep + 1);
			for(int j=i-l+1; j<=i; j++)	card[j] ++;
		}
	}
	l = 0;
	for(int i=3; i<=14; i++){ // 搜索双顺子 
		if(card[i] >= 2) l ++;
		else l = 0;
		if(l >= 3){ // 长度达到双顺子 
			for(int j=i-l+1; j<=i; j++)	card[j] -= 2;
			dfs(dep + 1);
			for(int j=i-l+1; j<=i; j++)	card[j] += 2;		
		}
	}
	l = 0;
	for(int i=3; i<=14; i++){ // 搜索三顺子 
		if(card[i] >= 3) l ++;
		else l = 0;
		if(l >= 2){ // 长度达到三顺子 
			for(int j=i-l+1; j<=i; j++)	card[i] -= 3;
			dfs(dep + 1);
			for(int j=i-l+1; j<=i; j++)	card[i] += 3;		
		}
	}
	for(int i=2; i<=14; i++){ // 搜三张牌或四张牌 
		if(card[i] >= 3){ //三带一张或一对 
			card[i] -= 3;
			for(int j=0; j<=14; j++){
				if(!card[j] || j == i) continue;
				if(card[j]){
					card[j] --;
					dfs(dep + 1);
					card[j] ++;
				}
				if(card[j] >= 2){
					card[j] -= 2;
					dfs(dep + 1);
					card[j] += 2;
				}
			}
			card[i] += 3; 
			if(card[i] == 4){ // 四带两张或两对 
				card[i] -= 4;
				for(int j=0; j<14; j++){ //带两张 
					if(!card[j] || j == i) continue;
					card[j] --;
					for(int k=0; k<14; k++){
						if(!card[k] || k == j || k == i) continue;
						card[k] --;
						dfs(dep + 1);
						card[k] ++;
					}
					card[j] ++;
				} 
				for(int j=0; j<14; j++){ //带两对 
					if(card[j] < 2 || j == i) continue;
					card[j] -= 2;
					for(int k=0; k<14; k++){
						if(card[k] < 2 || k == j || k == i) continue;
						card[k] -= 2;
						dfs(dep + 1);
						card[k] += 2;
					}
					card[j] += 2;
				} 
				card[i] += 4;
			}
		}
	}
	
	for(int i=0; i<=14; i++){ // 把剩余的牌打出 
		if(card[i]) dep ++; // 一次性打出所有相同点数的牌
		if(card[i] >= 3) return; // 如果有三张牌或四张牌没出,一定不是最优 
	}
	if(card[0] && card[1]) dep --; // 双王可以同时打出 
//	if(dep < ans) dbg(); 
	ans = ans < dep ? ans : dep; // ans取最小 
}

void input(){
	for(int i=0; i<n; i++){
		scanf("%d%d", &num, &ul); // 花色并用不到(useless) 
		if(num == 1) num = 14; // 由于A排在K后面,14存储A 
		if(num == 0 && ul == 2) num = 1; // 双王分开存储,防止被算成一对
		card[num] ++;
	}
}
void work(){	
	if(n <= 4){ // 暴力 
		power_sol();
		return;
	}
	while(T--){
		init();
		input();
//		dbg();
		dfs(0);
		printf("%d\n", ans);
	}  
} 
int main(){
	cin >> T >> n;
	work();
	return 0;
} 

更新记录


记录一下,从10.31 16:32 开始改这道题。

upd 10.31 18:45:吃完饭瞅了一下之前模拟赛的时候写炸的搜索,(已经看不懂了

upd 10.31 22:16:这个月内看来是做不完了(笑 各位Happy Halloween

upd 11.1 14:57:下午翘课来机房。听取WA声一片。重写。

upd 11.3 13:19:咕咕咕(搜索思路错了 重写

upd 11.3 21:47:我过了!我过了!(作死提交了下增强数据版)(WA+TLE 80pts)(“fxxk”)

posted @ 2019-10-31 22:21  mzWyt  阅读(145)  评论(2编辑  收藏  举报