zoukankan      html  css  js  c++  java
  • 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
    ");
    			else printf("2
    ");
    		}
    		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
    ");
    			else if(c1 == c2 && c2 != c3) printf("2
    ");
    			else if(c1 != c2 && c2 == c3) printf("2
    ");
    			else if(c1 == c3 && c2 != c3) printf("2
    ");
    			else printf("3
    ");
    		}
    		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
    ");
    			else if(c1==c2&&c2==c3&&c3!=c4) printf("1
    ");
    			else if(c1==c2&&c2!=c3&&c2==c4) printf("1
    ");
    			else if(c1!=c2&&c2==c3&&c3==c4) printf("1
    ");
    			else if(c1==c2&&c3==c4) printf("2
    ");
    			else if(c1==c3&&c2==c4) printf("2
    ");
    			else if(c1==c4&&c2==c3) printf("2
    ");
    			else if(c1==c2||c1==c3||c1==c4||c2==c3||c2==c4||c3==c4) printf("3
    ");
    			else printf("4
    ");			
    		}
    		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 << '	' << card[i] << '
    ';
    	}
    	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
    ", 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”)

  • 相关阅读:
    C#:基于WMI查询USB设备信息 及 Android设备厂商VID列表
    C#中 @ 的3种用途
    有关于 使用 命名管道 进行网络 进程间通信 的资料收集
    MySql LAST_INSERT_ID 【插入多条数据时】
    两个“不合理继承 ”的判定标识
    MYSQL 函数 字符串到整数
    Spring MVC 对于@ModelAttribute 、@SessionAttributes 的详细处理流程
    重构,拥有多个构造函数(重载)的类
    vue二级联动select
    gulp.dest用法详解
  • 原文地址:https://www.cnblogs.com/miserweyte/p/11774250.html
Copyright © 2011-2022 走看看