P2482 [SDOI2010] 猪国杀 斗地主加强版

P2482 [SDOI2010] 猪国杀

AC于2022年8月9日,代码写了一个晚上

点击查看代码
#include <stdio.h>
#include <string.h>
const int N = 15, M = 2005;
struct Pig {
	char type; // 类型
	bool showed; // 暴露身份
	bool fan_likely; // 被认为是类反猪
	bool have_weapon; // 有猪哥连弩
	bool alive; // 活着
	int tili; // 体力
	int card_count; // 拿过的牌的个数
	char cards[M]; // 拿过的牌, 若用过,标记为X
	bool Zhong() { return showed && type == 'Z'; } // 是否跳忠
	bool Fan() { return showed && type == 'F'; } // 是否跳反
} pigs[N];
int n, m, card_index;
int FanPigCount; // 活着的反猪个数
char remain_cards[M];
char winner;
// 读入
void input() {
	scanf("%d%d", &n, &m);
	char tp[3], card[2];
	for(int i = 1; i <= n; i ++) {
		scanf("%s", tp);
		pigs[i].type = *tp; // 猪的类型
		pigs[i].card_count = 4; // 猪的牌
		pigs[i].fan_likely = false; // 不是类反猪
		pigs[i].have_weapon = false; // 没有武器
		pigs[i].showed = i == 1; // 是否暴露
		pigs[i].alive = true; // 还活着
		pigs[i].tili = 4; // 初始体力
		if(*tp == 'F') FanPigCount ++;
		for(int j = 1; j <= 4; j ++)
			scanf("%s", card), pigs[i].cards[j] = *card; // 原始的牌
	}
	for(int i = 1; i <= m; i ++)
		scanf("%s", card), remain_cards[i] = *card; // 牌堆
}
// 拿一张牌
void get_a_card(int id) {
	if(card_index != m) card_index ++; // 没有用完
	pigs[id].cards[++ pigs[id].card_count] = remain_cards[card_index];
}
int next_alive(int id) {
	for(int id2 = id + 1; id2 != id; id2 ++, id2 > n && (id2 = 1))
		if(pigs[id2].alive) return id2;
	return 0;
}
// 体力变为0时的处理
void killed(int id, int killer) {
	// 先判断是否有桃子P(共性1)
	for(int i = 1; i <= pigs[id].card_count; i ++) // 枚举是否有桃子
		if(pigs[id].cards[i] == 'P') {
			pigs[id].tili ++; // 体力++
			pigs[id].cards[i] = 'X'; // 用过了
			if(pigs[id].tili > 0) return; // 活了!
		}
	pigs[id].alive = false; // 死了
	if(pigs[id].type == 'M') { // 主猪死亡
		winner = 'F'; // 反猪胜利
	} else if(pigs[id].type == 'Z') { // 忠猪死亡
		if(pigs[killer].type == 'M') { // 主猪干的
			pigs[killer].card_count = 0; // 牌没了
			pigs[killer].have_weapon = false; // 装备没了
		}
	} else if(pigs[id].type == 'F') { // 反猪死亡
		FanPigCount --;
		if(FanPigCount == 0) // 反猪全完
			winner = 'M'; // 主猪胜利
		else get_a_card(killer), get_a_card(killer), get_a_card(killer); // 拿3张牌
	}
}
// 吃桃子P, 共性1:吃掉
bool use_P(int id) {
	if(pigs[id].tili < 4) {
		pigs[id].tili ++;
		return true; // 用了桃子
	} return false;
}
// 闪D 若没有,则体力--
void use_D(int id) {
	for(int i = 1; i <= pigs[id].card_count; i ++)
		if(pigs[id].cards[i] == 'D') {
			pigs[id].cards[i] = 'X'; // 被用了
			return;
		}
	pigs[id].tili --;
}
// 杀K, 只能杀下一只猪
bool use_K(int id) {
	if(pigs[id].type == 'M') { // 主猪用杀
		int id2 = next_alive(id);
		if(pigs[id2].Fan() || pigs[id2].fan_likely) { // 反猪或类反猪
			use_D(id2); // 用闪回击
			if(pigs[id2].tili <= 0) killed(id2, id); // 被杀了
			return true; // 用了杀
		}
	} else if(pigs[id].type == 'Z') { // 忠猪
		int id2 = next_alive(id);
		if(pigs[id2].Fan()) {
			pigs[id].showed = true, pigs[id].fan_likely = false; // 暴露身份
			use_D(id2);
			if(pigs[id2].tili <= 0) killed(id2, id);
			return true;
		}
	} else if(pigs[id].type == 'F') { // 反猪
		int id2 = next_alive(id);
		if(pigs[id2].Zhong() || pigs[id2].type == 'M') { // 主猪或忠猪
			pigs[id].showed = true, pigs[id].fan_likely = false; // 暴露身份
			use_D(id2);
			if(pigs[id2].tili <= 0) killed(id2, id);
			return true;
		}
	}
	return false;
}
// 无懈可击, id表示使用锦囊的猪, target 表示目标方 1表示主猪方, 2表示反猪方
bool use_J(int id, int target) {
	int id2 = id; // 注意: 自己也可以用无懈可击
	do {
		if(pigs[id2].alive) {
			if((pigs[id2].type == 'F' && target == 2) || (pigs[id2].type != 'F' && target == 1)) {
				// 反猪帮反猪 或 忠猪帮主猪
				for(int i = 1; i <= pigs[id2].card_count; i ++)
					if(pigs[id2].cards[i] == 'J') {
						pigs[id2].showed = true, pigs[id2].fan_likely = false; // 暴露身份
						pigs[id2].cards[i] = 'X'; // 被用了
						return !use_J(id2, 3 - target); // 轮到对方无懈可击
					}
			}
		}
		id2 ++, id2 > n && (id2 = 1);
	} while(id2 != id);
	return false; // 用不了
}
void swap(int &x, int &y) { static int z; z = x, x = y, y = z; }
void fight(int id, int id2) {
	if(pigs[id].type == 'M' && pigs[id2].type == 'Z') { // 主猪和忠猪决斗: 忠猪绝不出牌
		pigs[id2].tili --;
		if(pigs[id2].tili <= 0) killed(id2, id);
	} else { // 不遗余力出牌
		int now = id2, other = id; // 现在出牌的人, 另一个人
		while(true) {
			bool found = false; // 找到杀并使用
			for(int i = 1; i <= pigs[now].card_count; i ++)
				if(pigs[now].cards[i] == 'K') {
					pigs[now].cards[i] = 'X';
					found = true;
					break;
				}
			if(found) swap(now, other); // 轮流出牌
			else {
				pigs[now].tili --;
				if(pigs[now].tili <= 0) killed(now, other); // 被杀了
				return;
			}
		}
	}
}
// 决斗F
bool use_F(int id) {
	if(pigs[id].type == 'M') { // 主猪打反猪与类反猪
		for(int id2 = id + 1; id2 != id; id2 ++, id2 > n && (id2 = 1))
			if(pigs[id2].alive && (pigs[id2].Fan() || pigs[id2].fan_likely)) {
				if(!pigs[id2].showed || !use_J(id, 2)) fight(id, id2); // fight:打架
				return true; // 注意: 没有暴露身份的对手的不能用无懈可击
			}
	} else if(pigs[id].type == 'Z') { // 忠猪找反猪
		for(int id2 = id + 1; id2 != id; id2 ++, id2 > n && (id2 = 1))
			if(pigs[id2].alive && pigs[id2].Fan()) {
				if(!pigs[id2].showed || !use_J(id, 2)) fight(id, id2);
				pigs[id].showed = true; // 身份暴露
				return true;
			}
	} else if(pigs[id].type == 'F') { // 反猪找主猪
		if(!use_J(id, 1)) fight(id, 1); // 主猪身份明确, 可以使用无懈可击
		pigs[id].showed = true; // 身份暴露
		return true;
	}
	return false;
}
// 南猪入侵
bool use_N(int id) {
	for(int id2 = id + 1; id2 != id; id2 ++, id2 > n && (id2 = 1))
		if(pigs[id2].alive) {
			if(pigs[id2].showed && use_J(id, pigs[id2].type == 'F' ? 2 : 1)) continue; // 被无懈可击了
			bool hurt = true; // 对id2产生伤害
			for(int i = 1; i <= pigs[id2].card_count; i ++)
				if(pigs[id2].cards[i] == 'K') {
					pigs[id2].cards[i] = 'X';
					hurt = false;
					break;
				}
			if(hurt) { // 造成伤害
				pigs[id2].tili --;
				if(pigs[id2].tili <= 0) killed(id2, id); // 被杀了
				if(winner) return true; // 立即退出
				if(id2 == 1 && !pigs[id].showed) pigs[id].fan_likely = true; // 主猪被未表明身份的伤害:类反猪
			}
		}
	return true;
}
bool use_W(int id) {
	for(int id2 = id + 1; id2 != id; id2 ++, id2 > n && (id2 = 1))
		if(pigs[id2].alive) {
			if(pigs[id2].showed && use_J(id, pigs[id2].type == 'F' ? 2 : 1)) continue; // 被无懈可击了
			bool hurt = true; // 对id2产生伤害
			for(int i = 1; i <= pigs[id2].card_count; i ++)
				if(pigs[id2].cards[i] == 'D') {
					pigs[id2].cards[i] = 'X';
					hurt = false;
					break;
				}
			if(hurt) { // 造成伤害
				pigs[id2].tili --;
				if(pigs[id2].tili <= 0) killed(id2, id); // 被杀了
				if(winner) return true; // 立即退出
				if(id2 == 1 && !pigs[id].showed) pigs[id].fan_likely = true; // 主猪被未表明身份的伤害:类反猪
			}
		}
	return true;
}
int main() {
	input();
	while(true) for(int id = 1; id <= n; id ++) if(pigs[id].alive) {
		get_a_card(id), get_a_card(id); // 摸牌
		bool can_use_K = true;
		for(int i = 1; i <= pigs[id].card_count; i ++) {
			if(pigs[id].cards[i] == 'X') continue;
			bool used_card = false; // 是否使用牌
			if(pigs[id].cards[i] == 'P') used_card = use_P(id); // 能够吃桃子
			else if(pigs[id].cards[i] == 'K') {
				if(pigs[id].have_weapon) can_use_K = true; // 有武器: 能够用任意次杀
				if(can_use_K) {
					used_card = use_K(id); // 杀
					if(used_card) can_use_K = false; // 用了
				}
			} else if(pigs[id].cards[i] == 'F') used_card = use_F(id);
			else if(pigs[id].cards[i] == 'N') used_card = use_N(id);
			else if(pigs[id].cards[i] == 'W') used_card = use_W(id);
			else if(pigs[id].cards[i] == 'Z') used_card = pigs[id].have_weapon = true; // 猪哥连弩
			if(used_card) pigs[id].cards[i] = 'X', i = 0; // 用过了, i=0是因为前面的牌可能可以用
			if(!pigs[id].alive) break; // 猪死了, 完
			if(winner) goto GAME_OVER; // 立即退出
		}
	}
	GAME_OVER: printf("%cP\n", winner);
	for(int id = 1; id <= n; id ++)
		if(pigs[id].alive) {
			for(int i = 1; i <= pigs[id].card_count; i ++)
				if(pigs[id].cards[i] != 'X') printf("%c ", pigs[id].cards[i]);
			putchar(10);
		} else puts("DEAD");
	return 0;
}

P2540 NOIP2015 提高组 斗地主 加强版

2002/8/29 完成P2668斗地主和加强版

点击查看代码
// DFS求出顺子的方案
// 预处理DP求出其余牌的答案(这些牌与大小无关)
#include <stdio.h>
#include <string.h>

const int N = 37;

int T, n, res;
int f[N][N][N][N][3]; // 出现过1,2,3,4次的有a,b,c,d个,王有e个
int num[N]; // 每个数字有几个

bool upd(int &v, int val) { return v > val && (v = val); }

void dp() {
	memset(f, 0x3f, sizeof(f)), f[0][0][0][0][0] = 0;
	for(int d = 0; d <= 25; d ++)
	 for(int c = 0; c <= 25; c ++)
	  for(int a = 0; a <= 25; a ++)
	   for(int b = 0; b <= 25; b ++)
		for(int e = 0; e <= 2; e ++) {
			int &v = f[a][b][c][d][e];
			if(a) upd(v, f[a - 1][b][c][d][e] + 1);
			if(b) upd(v, f[a][b - 1][c][d][e] + 1);
			if(c) upd(v, f[a][b][c - 1][d][e] + 1);
			if(d) upd(v, f[a][b][c][d - 1][e] + 1);
			if(e) upd(v, f[a][b][c][d][e - 1] + 1);
			// 这些都是没有带牌的情况
			if(a && c) upd(v, f[a - 1][b][c - 1][d][e] + 1);
			if(c && e) upd(v, f[a][b][c - 1][d][e - 1] + 1);
			// 这些是三带一
			if(b && d) upd(v, f[a][b - 1][c][d - 1][e] + 1); // 一对变两张
			if(b > 1 && d) upd(v, f[a][b - 2][c][d - 1][e] + 1); // 两对
			if(d && e > 1) upd(v, f[a][b][d][d - 1][e - 2] + 1); // 两王变两张
			if(d > 1) upd(v, f[a][b][c][d - 2][e] + 1); // 炸弹变两对
			if(a > 1 && d) upd(v, f[a - 2][b][c][d - 1][e] + 1); // 两对
			if(a && d && e) upd(v, f[a - 1][b][c][d - 1][e - 1] + 1); // 一张+一王
			// 这些是四带二
			if(b && c) upd(v, f[a][b - 1][c - 1][d][e] + 1);
			// 三带二
			if(c) upd(v, f[a + 1][b + 1][c - 1][d][e]); // 拆牌※※※
			if(d) upd(v, f[a + 1][b][c + 1][d - 1][e]); // 拆牌
			if(e > 1) upd(v, f[a][b][c][d][e - 2] + 1); // 火箭
		}
}

void dfs(int dep) {
	if(dep >= res) return; // 不够优秀
	for(int t = 1; t <= 3; t ++) { // 枚举顺牌中有多少个
		int l = (int[4]){0, 5, 3, 2}[t];
		for(int i = 0; i <= 11; i ++) {
			int len = 0;
			while(num[i + len] >= t && i + len <= 11) len ++;
			for(int l2 = len; l2 >= l; l2 --) {
				int j = i + l2 - 1;
				for(int k = i; k <= j; k ++) num[k] -= t;
				dfs(dep + 1);
				for(int k = i; k <= j; k ++) num[k] += t;
			}
		}
	}
	int cnt[5] = {0};
	for(int i = 0; i <= 12; i ++) cnt[num[i]] ++;
	int now = dep + f[cnt[1]][cnt[2]][cnt[3]][cnt[4]][num[13]];
	if(res > now) res = now;
}

int main() {
	scanf("%d%d", &T, &n);
	dp();
	while(T --) {
		memset(num, 0, sizeof(num)), res = 1 << 30;
		for(int i = 1, a, b; i <= n; i ++) {
			scanf("%d%d", &a, &b);
			if(a == 0) num[13] ++;
			else num[a <= 2 ? a + 10 : a - 3] ++;
		}
		dfs(0), printf("%d\n", res);
	}
	return 0;
}

posted @   azzc  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示