[SDOI2010] 猪国杀

猪国杀

前言

这道题是一道大模拟,个人认为还是挺锻炼码力的,所以本蒟蒻花一天的时间,爆肝一周的时间终于写完了。。。

题意

题目传送门

游戏目的

主猪 / \(\texttt{MP}\):自己存活的情况下消灭所有的反猪。
忠猪 / \(\texttt{ZP}\):不惜一切保护主猪,胜利条件与主猪相同。
反猪 / \(\texttt{FP}\):杀死主猪。

游戏过程

游戏开始时,每个玩家手里都会有 \(4\) 张牌,且体力上限和初始体力都是 \(4\)

开始游戏时,从主猪开始,按照逆时针方向(数据中就是按照编号从 \(1 , 2, 3 \ldots n , 1 \ldots\) 的顺序)依次行动。

每个玩家自己的回合可以分为 \(2\) 个阶段:

  • 摸牌阶段:从牌堆顶部摸 \(2\) 张牌,依次放到手牌的最右边;
  • 出牌阶段:你可以使用任意张牌,每次使用牌的时候都使用最靠左的能够使用的牌。当然,要满足如下规则:
    1. 如果没有猪哥连弩,每个出牌阶段只能使用 \(1\) 次「杀」来攻击;
    2. 任何牌被使用后被弃置(武器是装备上);被弃置的牌以后都不能再用,即与游戏无关。

牌的种类

基本牌

  • 『桃 / \(\texttt{P}\)』在自己的回合内,如果自己的体力值不等于体力上限,那么使用 \(1\) 个桃可以为自己补充 \(1\) 点体力,否则不能使用桃;桃只能对自己使用;在自己的回合外,如果自己的血变为 \(0\) 或者更低,那么也可以使用。

  • 『杀 / \(\texttt{K}\)』在自己的回合内,对攻击范围内除自己以外的 \(1\) 名角色使用。如果没有被『闪』抵消,则造成 \(1\) 点伤害。无论有无武器,杀的攻击范围都是 \(1\)

  • 『闪 / \(\texttt{D}\)』当你受到杀的攻击时,可以弃置 \(1\) 张闪来抵消杀的效果。

锦囊牌

  • 『决斗 / \(\texttt{F}\)』出牌阶段,对除自己以外任意 \(1\) 名角色使用,由目标角色先开始,自己和目标角色轮流弃置 \(1\) 张杀,首先没有杀可弃的一方受到 \(1\) 点伤害,另一方视为此伤害的来源。

  • 『南猪入侵 / \(\texttt{N}\)』出牌阶段,对除你以外所有角色使用,按逆时针顺序从使用者下家开始依次结算,除非弃置 \(1\) 张杀,否则受到 \(1\) 点伤害。

  • 『万箭齐发 / \(\texttt{W}\)』和南猪入侵类似,不过要弃置的不是杀而是闪。

  • 『无懈可击 / \(\texttt{J}\)』在目标锦囊生效前抵消其效果。每次有 \(1\) 张锦囊即将生效时,从使用这张锦囊的猪开始,按照逆时针顺序,依次得到使用无懈可击的机会;效果:用于决斗时,决斗无效并弃置;用于南猪入侵或万箭齐发时,当结算到某个角色时才能使用,当前角色不需弃置牌并且不会受到伤害(仅对 \(1\) 个角色产生效果);用于无懈可击时,成为目标的无懈可击被无效。

装备牌

  • 『猪哥连弩 / \(\texttt{Z}\)』武器,攻击范围 \(1\) ,出牌阶段你可以使用任意张杀; 同一时刻最多只能装 \(1\) 把武器;如果先前已经有了 \(1\) 把武器,那么之后再装武器的话,会弃置以前的武器来装现在的武器。

特殊事件及概念解释

  • 伤害来源:杀、南猪入侵、万箭齐发的伤害来源均是使用该牌的猪,决斗的伤害来源如上;

  • 距离:两只猪的距离定义为沿着逆时针方向间隔的猪数 \(+1\) 。即初始时 \(1\)\(2\) 的距离为 \(1\) ,但是 \(2\)\(1\) 的距离就是 \(n-1\) 。注意一个角色的死亡会导致一些猪距离的改变;

  • 玩家死亡:如果该玩家的体力降到 \(0\) 或者更低,并且自己手中没有足够的桃使得自己的体力值回到 \(1\) ,那么就死亡了,死亡后所有的牌(装备区,手牌区)被弃置;

  • 奖励与惩罚:反猪死亡时,最后一个伤害来源处(即使是反猪)立即摸 \(3\) 张牌。忠猪死亡时,如果最后一个伤害来源是主猪,那么主猪所有装备牌、手牌被弃置。

注意:一旦达成胜利条件,游戏立刻结束,因此即使会摸 \(3\) 张牌或者还有牌可以用也不用执行了。

现在,我们已经知道每只猪的角色、手牌,还有牌堆初始情况,并且假设每个角色会按照如下的行为准则进行游戏,你需要做的就是告诉小猪 iPig 最后的结果。

几种行为

  • 献殷勤:使用无懈可击挡下南猪入侵、万箭齐发、决斗;使用无懈可击抵消表敌意;
  • 表敌意:对某个角色使用杀、决斗;使用无懈可击抵消献殷勤;
  • 跳忠:即通过行动表示自己是忠猪。跳忠行动就是对主猪或对某只已经跳忠的猪献殷勤,或者对某只已经跳反的猪表敌意;
  • 跳反:即通过行动表示自己是反猪。跳反行动就是对主猪或对某只已经跳忠的猪表敌意,或者对某只已经跳反的猪献殷勤。

注意:忠猪不会跳反,反猪也不会跳忠;不管是忠猪还是反猪,能够跳必然跳

行动准则

共性

  • 每个角色如果手里有桃且生命值未满,那么必然吃掉;
  • 有南猪入侵、万箭齐发、必然使用;有装备必然装上;
  • 受到杀时,有闪必然弃置;
  • 响应南猪入侵或者万箭齐发时候,有杀 / 闪必然弃置;
  • 不会对未表明身份的猪献殷勤(包括自己)。

特性

  • 主猪:
    • 主猪会认为「没有跳身份,且用南猪入侵 / 万箭齐发对自己造成伤害的猪」是反猪(没伤害到不算,注意类反猪并没有表明身份),如果之后跳了,那么主猪会重新认识这只猪;
    • 对于每种表敌意的方式,对逆时针方向能够执行到的第一只类反猪或者已跳反猪表;如果没有,那么就不表敌意;
    • 决斗时会不遗余力弃置杀;
    • 如果能对已经跳忠的猪或自己献殷勤,那么一定献;如果能够对已经跳反的猪表敌意,那么一定表。
  • 忠猪:
    • 对于每种表敌意的方式,对「逆时针方向能够执行到的第一只已经跳反的猪」表,如果没有,那么就不表敌意;
    • 决斗时,如果对方是主猪,那么不会弃置杀,否则,会不遗余力弃置杀;
    • 如果有机会对主猪或者已经跳忠的猪献殷勤,那么一定献。
  • 反猪:
    • 对于每种表敌意的方式,如果有机会则对主猪表,否则,对「逆时针方向能够执行到的第一只已经跳忠的猪」表,如果没有,那么就不表敌意;
    • 决斗时会不遗余力弃置杀
    • 如果有机会对已经跳反的猪献殷勤,那么一定献

做法

本蒟蒻刚刚看到题一脸懵逼,主要是卡在了无懈可击的生效前的意思,举个例子,\(A\) 使出了『南猪入侵』,逆时针结算到 \(B\) 时,若 \(A\)\(B\) 不是相同的组别,与 \(A\) 一队的猪就会努力使锦囊生效,与 \(B\) 为一个组别的猪就会想法设法的使锦囊失效。

其中这题的部分分为:

  • \(10\) 分:没有锦囊牌。
  • \(30\) 分:没有无懈可击。
  • \(100\) 分:\(n \le10\)\(m \le 1000\)

\(10\) 分,没有锦囊牌

辅助数组

一下只列举出比较重要的几个函数。

  1. string pig[i]:表示第 \(i\) 只猪的身份。
  2. char c[i][j]:表示第 \(i\) 只猪的第 \(j\) 张卡牌是多少。
  3. int bld[i]:blood 的缩写,意思为第 \(i\) 只猪的血量。
  4. int tot[i]:表示第 \(i\) 只猪的总卡牌数量(被用掉的也算),主要是为了实现一些卡牌删除与加入。
  5. int lpig[i]:表示第 \(i\) 只猪的左边一只猪是几号,其中 \(1\) 的左边是 \(n\)
  6. int rpig[i]:表示第 \(i\) 只猪的右边一只猪是几号,其中 \(n\) 的右边是 \(1\)

其中 lpig[i]rpig[i] 为类似双链表的东西。

  1. bool tfpig[i]:表示第 \(i\) 只猪是否为类反猪。
  2. bool iden[i]:表示第 \(i\) 只猪是否跳身份。
  3. bool flag[i][j]:表示第 \(i\) 只猪的第 \(j\) 张卡牌是否被用。

辅助函数

  1. count(x, t):表示第 \(i\) 只猪的卡牌中有几张为 \(t\) 的卡牌。
int count(int x, char t) { 
	int res = 0;
	for (int i = 1; i <= tot[x]; i++)
		if (!flag[x][i] && c[x][i] == t)
			res++;
	return res;
}
  1. del(x, k, t):从c[x]中删除k个字符t。
void del(int x, int k, char t) { 
	if (k <= 0) return;
	int cnt = 0;
	for (int i = 1; i <= tot[x] && cnt < k; i++)
		if (!flag[x][i] && c[x][i] == t)
			flag[x][i] = true, cnt++;
}
  1. peach(i):在自己的回合回血。
void peach(int x) {
	if (bld[x] < 4) { bld[x]++; del(x, 1, 'P'); }
}
  1. bld_back(i):在别人的回合内回血。
bool bld_back(int x) {
	if (bld[x] > 0) return false;
	bool ok = false; // 是否阵亡
	int P = count(x, 'P');
	if (abs(bld[x]) + 1 <= P) { del(x, abs(bld[x]) + 1, 'P'); bld[x] = 1; }
	else { ok = true; thrw(x); }
	return ok;
}

要注意 peach 和 bld_back 这两个函数的区别:peach 是最多只能回到 \(4\),而 bld_back 是当前这头猪在血量小于等于 \(0\) 时,用桃将其血量回到 \(1\)
5. Dead(x, y):表示 \(x\)\(y\) 弄(可能是南猪入侵、万箭齐发、决斗或者杀)死后的处理:

  • \(pig[y]\) 为主猪,直接输出
  • \(pig[y]\) 为反猪,若反猪数量为 \(0\),直接输出,否则 \(x\)\(3\) 张牌。
  • \(pig[x]\) 为主猪,则将主猪的所有牌弃置。
void Dead(int x, int y) {
	dead[y] = true;
	rpig[lpig[y]] = rpig[y];
	lpig[rpig[y]] = lpig[y];
	if (x == y) return;
	if (pig[y] == "MP") { Print(); exit(0); }
	else if (pig[y] == "FP") {
		if (--fpig == 0) { Print(); exit(0); }
		In(x, 3);
	} else if (pig[x] == "MP") thrw(x);
}

其中 In(i, 3) 表示 \(i\) 加入两张牌,thrw(i):表示将 \(i\) 的所有牌弃置(包括装备牌)。

将以上的代码组成一个程序即可,这里就不在做详细的解释了,但是为了代码简洁清晰,强烈推荐使用switch case

代码

#include <iostream>
#include <algorithm>
using namespace std;
int n, m, bld[15], fpig, idx = 0, tot[15], rpig[15], lpig[15];
char c[15][10010], card[10010];
bool tfpig[15], arm[15], flag[15][10010], iden[15], dead[15];
string pig[15];
// c[i][j]:表示第i只猪的第j张牌为c[i][j]。
// tot[i]:表示第i只猪有多少张牌。
// pig[i]:表示第i只猪的角色。
// bld[i]:表示第i只猪的血量值。
// fpig:表示活的反猪的数量。
// tfpig[i]:表示第i只猪是否为类反猪。
// arm[i]:表示第i只猪是否有装备。
// flag[i][j]:表示第i只猪的第j张牌是否被用过。
// iden[i]:表示第i只猪是否已经跳身份。
void Read() {
	iden[1] = true;
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		cin >> pig[i];
		bld[i] = 4;
		for (int j = 1; j <= 4; j++) cin >> c[i][++tot[i]];
		if (pig[i] == "FP") fpig++;
		if (i == n) rpig[i] = 1;
		else rpig[i] = i + 1;
		if (i == 1) lpig[i] = n;
		else lpig[i] = i - 1;
	}
	for (int i = 1; i <= m; i++) cin >> card[i];
} // 读入
void Print() {
	if (fpig == 0) cout << "MP" << endl;
	else cout << "FP" << endl;
	for (int i = 1; i <= n; i++)
		if (dead[i]) cout << "DEAD" << endl;
		else {
			int cnt = 0, res = 0;
			for (int j = 1; j <= tot[i]; j++) if (!flag[i][j]) cnt++;
			for (int j = 1; j <= tot[i]; j++)
				if (!flag[i][j] && res != cnt - 1) cout << c[i][j] << " ";
				else if (!flag[i][j] && res == cnt - 1) cout << c[i][j];
			if (!cnt) cout << " ";
			cout << endl;
		}
}
void thrw(int x) {
	for (int i = 1; i <= tot[x]; i++) flag[x][i] = true;
	arm[x] = false;
} // 将第i只猪的手牌和装备弃置
void In(int x, int k) { 
	for (int i = idx + 1; i <= idx + k; i++) c[x][++tot[x]] = card[min(m, i)];
	idx += k;
}// 摸k张牌
int count(int x, char t) { 
	int res = 0;
	for (int i = 1; i <= tot[x]; i++)
		if (!flag[x][i] && c[x][i] == t)
			res++;
	return res;
}// 统计第i只猪有几张t这张牌
void del(int x, int k, char t) { 
	if (k <= 0) return;
	int cnt = 0;
	for (int i = 1; i <= tot[x] && cnt < k; i++)
		if (!flag[x][i] && c[x][i] == t)
			flag[x][i] = true, cnt++;
}// 从c[x]中删除k个字符t。
void peach(int x) {
	if (bld[x] < 4) { bld[x]++; del(x, 1, 'P'); }
} // 在自己的回合回血
bool bld_back(int x) {
	if (bld[x] > 0) return false;
	bool ok = false; // 是否阵亡
	int P = count(x, 'P');
	if (abs(bld[x]) + 1 <= P) { del(x, abs(bld[x]) + 1, 'P'); bld[x] = 1; }
	else { ok = true; thrw(x); }
	return ok;
} // 在别人的回合回血
void Dead(int x, int y) {
	dead[y] = true;
	rpig[lpig[y]] = rpig[y];
	lpig[rpig[y]] = lpig[y];
	if (x == y) return;
	if (pig[y] == "MP") { Print(); exit(0); }
	else if (pig[y] == "FP") {
		if (--fpig == 0) { Print(); exit(0); }
		In(x, 3);
	} else if (pig[x] == "MP") thrw(x);
} // x杀死了y。
void kill(int i, int pos) {
	iden[i] = true;
	int D = count(pos, 'D'); // 统计闪的数量
	int K = 1;
	if (D >= 1) { K = 0; del(pos, 1, 'D'); }
	del(i, 1, 'K');
	bld[pos] -= K;
	if (bld[pos] <= 0) { bool f = bld_back(pos); if (f) Dead(i, pos); } // 对pos这头猪回血
} // i对pos使用杀

void Arm(int x) { arm[x] = true; del(x, 1, 'Z'); }
int find(string op, int i) {
	int pos = -1;
	if (i == n && op == "FP") pos = 1; // 特判
	for (int j = i + 1; j <= n && pos == -1; j++)
		if (op == "MP" && (pig[j] == "FP" && iden[j] || tfpig[j]) && bld[j] > 0) { pos = j; break; }
		else if (op == "ZP" && pig[j] == "FP" && iden[j] && bld[j] > 0) { pos = j; break; }
		else if (op == "FP" && pig[j] == "ZP" && iden[j] && bld[j] > 0) { pos = j; break; }
	for (int j = 1; j < i && pos == -1; j++)
		if (op == "MP" && (pig[j] == "FP" && iden[j] || tfpig[j]) && bld[j] > 0) { pos = j; break; }
		else if (op == "ZP" && pig[j] == "FP" && iden[j] && bld[j] > 0) { pos = j; break; }
		else if (op == "FP" && pig[j] == "ZP" && iden[j] && bld[j] > 0) { pos = j; break; }
	return pos;
} // 找到对应种类的猪所要找到的杀的对象,没找到返回-1。
bool find_to_kill(string op, int x) {
	bool can = false;
	string nxt = pig[rpig[x]];
	if (op == "MP" && (nxt == "FP" && iden[rpig[x]] || tfpig[rpig[x]])) can = true;
	else if (op == "ZP" && nxt != "MP" && iden[rpig[x]] && pig[rpig[x]] == "FP") can = true;
	else if (op == "FP" && nxt != "FP" && (rpig[x] == 1 || iden[rpig[x]] && pig[rpig[x]] == "ZP")) can = true;
	return can;
}
void play(int x) { // 模拟
	In(x, 2);
	bool ok = false, use_K = false;
	for (int i = 1; i <= tot[x]; i++) {
		if (flag[x][i]) continue;
		char now = c[x][i];
		switch (now) {
			case 'P': peach(x); break;
			case 'K':
				if (find_to_kill(pig[x], x) && (!arm[x] && !use_K || arm[x])) { kill(x, rpig[x]), use_K = true; }
				break;
			case 'Z': Arm(x); break;
		}
		if (dead[x]) break;
	}
}
void work() {
	int i = 0;
	while (fpig && bld[1] > 0) {
		i = i % n + 1;
		if (dead[i]) continue;
		else play(i);
	}
}
int main() {
	Read();
	work();
	Print();
	return 0;
}

\(30\) 分,没有无懈可击

决斗

决斗说白了就是两个人同时弃置一张杀,被决斗者先出,谁先没有杀就扣一滴血。假设 \(x\)\(y\) 发起了决斗,分别统计 \(x\) 的杀的数量以及 \(y\) 杀的数量,记作 \(xK\)\(yK\)

  • \(xK \ge yK\):则 \(y\) 扣一滴血。
  • \(xK < yK\):则 \(x\) 扣一滴血。
void fight(int x, int y) {
	del(x, 1, 'F');
	iden[x] = true;
	if (pig[x] == "MP" && pig[y] == "ZP" && tfpig[y] && !iden[y]) {
		if (--bld[y] <= 0) { int f = bld_back(y); if (f) Dead(x, y); }
		return;
	}
	int xK = count(x, 'K');
	int yK = count(y, 'K');
	int man, toman;
	if (xK >= yK) { toman = x, man = y, bld[y]--; del(x, yK, 'K'), del(y, yK, 'K'); }
	else { man = x, toman = y, bld[x]--; del(y, xK + 1, 'K'), del(x, xK, 'K'); }
	if (bld[man] <= 0) { int f = bld_back(man); if (f) Dead(toman, man); }
}

南猪入侵

从使用者的下一位开始,依次弃置一张杀,若没有就扣一滴血。按意思模拟即可,但是当结算到第 \(i\) 头猪要判断下是否死亡。假设枚举到了第 \(i\) 只猪:

  • \(K\):啥事都不用干。
  • 没有 \(K\):扣一滴血,若 \(pig[i]\) 为主猪,则使用者将成为类反猪(若已经跳身份的不算)。
// 处理
void Initnanzhu(int j, int x) {
	if (bld[j] <= 0) return ;
	int K = count(j, 'K');
	if (K > 0) del(j, 1, 'K');
	else {
		bool f = true;
		if (--bld[j] <= 0) {
			int P = count(j, 'P');
			if (P == 0) Dead(x, j);
			else bld[j]++, del(j, 1, 'P'), f = false;
		}
		if (j == 1 && f) tfpig[x] = true;
	}
}
// 枚举
void nanzhu(int x) {
	del(x, 1, 'N');
	for (int j = x + 1; j <= n; j++) Initnanzhu(j, x);
	for (int j = 1; j < x; j++) Initnanzhu(j, x);
}

万箭齐发

同理南猪入侵,区别为将杀改成了闪。

// 处理
void Initwanjian(int j, int x) {
	if (dead[j]) return;
	int D = count(j, 'D');
	if (D > 0) del(j, 1, 'D'); 
	else {
		bool f = true;
		if (--bld[j] <= 0) {
			int P = count(j, 'P');
			if (P == 0) Dead(x, j);
			else bld[j]++, del(j, 1, 'P'), f = false;
		}
		if (j == 1 && f) tfpig[x] = true;
	}
}
// 枚举
void wanjian(int x) {
	del(x, 1, 'W');
	for (int j = x + 1; j <= n; j++) Initwanjian(j, x);
	for (int j = 1; j < x; j++) Initwanjian(j, x);
}

代码

#include <iostream>
#include <algorithm>
using namespace std;
int n, m, bld[15], fpig, idx = 0, tot[15], rpig[15], lpig[15];
char c[15][10010], card[10010];
bool tfpig[15], arm[15], flag[15][10010], iden[15], dead[15];
string pig[15];
// c[i][j]:表示第i只猪的第j张牌为c[i][j]。
// tot[i]:表示第i只猪有多少张牌。
// pig[i]:表示第i只猪的角色。
// bld[i]:表示第i只猪的血量值。
// fpig:表示活的反猪的数量。
// tfpig[i]:表示第i只猪是否为类反猪。
// arm[i]:表示第i只猪是否有装备。
// flag[i][j]:表示第i只猪的第j张牌是否被用过。
// iden[i]:表示第i只猪是否已经跳身份。
void Read() {
	iden[1] = true;
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		cin >> pig[i];
		bld[i] = 4;
		for (int j = 1; j <= 4; j++) cin >> c[i][++tot[i]];
		if (pig[i] == "FP") fpig++;
		if (i == n) rpig[i] = 1;
		else rpig[i] = i + 1;
		if (i == 1) lpig[i] = n;
		else lpig[i] = i - 1;
	}
	for (int i = 1; i <= m; i++) cin >> card[i];
} // 读入
void Print() {
	if (fpig == 0) cout << "MP" << endl;
	else cout << "FP" << endl;
	for (int i = 1; i <= n; i++)
		if (dead[i]) cout << "DEAD" << endl;
		else {
			int cnt = 0, res = 0;
			for (int j = 1; j <= tot[i]; j++) if (!flag[i][j]) cnt++;
			for (int j = 1; j <= tot[i]; j++)
				if (!flag[i][j] && res != cnt - 1) cout << c[i][j] << " ";
				else if (!flag[i][j] && res == cnt - 1) cout << c[i][j];
			if (!cnt) cout << " ";
			cout << endl;
		}
}
void thrw(int x) {
	for (int i = 1; i <= tot[x]; i++) flag[x][i] = true;
	arm[x] = false;
} // 将第i只猪的手牌和装备弃置
void In(int x, int k) { 
	for (int i = idx + 1; i <= idx + k; i++) c[x][++tot[x]] = card[min(m, i)];
	idx += k;
}// 摸k张牌
int count(int x, char t) { 
	int res = 0;
	for (int i = 1; i <= tot[x]; i++)
		if (!flag[x][i] && c[x][i] == t)
			res++;
	return res;
}// 统计第i只猪有几张t这张牌
void del(int x, int k, char t) { 
	if (k <= 0) return;
	int cnt = 0;
	for (int i = 1; i <= tot[x] && cnt < k; i++)
		if (!flag[x][i] && c[x][i] == t)
			flag[x][i] = true, cnt++;
}// 从c[x]中删除k个字符t。
void peach(int x) {
	if (bld[x] < 4) { bld[x]++; del(x, 1, 'P'); }
} // 在自己的回合回血
bool bld_back(int x) {
	if (bld[x] > 0) return false;
	bool ok = false; // 是否阵亡
	int P = count(x, 'P');
	if (abs(bld[x]) + 1 <= P) { del(x, abs(bld[x]) + 1, 'P'); bld[x] = 1; }
	else { ok = true; thrw(x); }
	return ok;
} // 在别人的回合回血
void Dead(int x, int y) {
	dead[y] = true;
	rpig[lpig[y]] = rpig[y];
	lpig[rpig[y]] = lpig[y];
	if (x == y) return;
	if (pig[y] == "MP") { Print(); exit(0); }
	else if (pig[y] == "FP") {
		if (--fpig == 0) { Print(); exit(0); }
		In(x, 3);
	} else if (pig[x] == "MP") thrw(x);
} // x杀死了y。
void kill(int i, int pos) {
	iden[i] = true;
	int D = count(pos, 'D'); // 统计闪的数量
	int K = 1;
	if (D >= 1) { K = 0; del(pos, 1, 'D'); }
	del(i, 1, 'K');
	bld[pos] -= K;
	if (bld[pos] <= 0) { bool f = bld_back(pos); if (f) Dead(i, pos); } // 对pos这头猪回血
} // i对pos使用杀
void fight(int x, int y) {
	del(x, 1, 'F');
	iden[x] = true;
	if (pig[x] == "MP" && pig[y] == "ZP" && tfpig[y] && !iden[y]) {
		if (--bld[y] <= 0) { int f = bld_back(y); if (f) Dead(x, y); }
		return;
	}
	int xK = count(x, 'K');
	int yK = count(y, 'K');
	int man, toman;
	if (xK >= yK) { toman = x, man = y, bld[y]--; del(x, yK, 'K'), del(y, yK, 'K'); }
	else { man = x, toman = y, bld[x]--; del(y, xK + 1, 'K'), del(x, xK, 'K'); }
	if (bld[man] <= 0) { int f = bld_back(man); if (f) Dead(toman, man); }
}// x向y发起决斗
void Initnanzhu(int j, int x) {
	if (bld[j] <= 0) return ;
	int K = count(j, 'K');
	if (K > 0) del(j, 1, 'K');
	else {
		bool f = true;
		if (--bld[j] <= 0) {
			int P = count(j, 'P');
			if (P == 0) Dead(x, j);
			else bld[j]++, del(j, 1, 'P'), f = false;
		}
		if (j == 1 && f) tfpig[x] = true;
	}
}
void Initwanjian(int j, int x) {
	if (dead[j]) return;
	int D = count(j, 'D');
	if (D > 0) del(j, 1, 'D'); 
	else {
		bool f = true;
		if (--bld[j] <= 0) {
			int P = count(j, 'P');
			if (P == 0) Dead(x, j);
			else bld[j]++, del(j, 1, 'P'), f = false;
		}
		if (j == 1 && f) tfpig[x] = true;
	}
}
void nanzhu(int x) {
	del(x, 1, 'N');
	for (int j = x + 1; j <= n; j++) Initnanzhu(j, x);
	for (int j = 1; j < x; j++) Initnanzhu(j, x);
} // x 发起的南猪入侵
void wanjian(int x) {
	del(x, 1, 'W');
	for (int j = x + 1; j <= n; j++) Initwanjian(j, x);
	for (int j = 1; j < x; j++) Initwanjian(j, x);
} // x 发起的万箭齐发
void Arm(int x) { arm[x] = true; del(x, 1, 'Z'); }
int find(string op, int i) {
	int pos = -1;
	if (i == n && op == "FP") pos = 1; // 特判
	for (int j = i + 1; j <= n && pos == -1; j++)
		if (op == "MP" && (pig[j] == "FP" && iden[j] || tfpig[j]) && bld[j] > 0) { pos = j; break; }
		else if (op == "ZP" && pig[j] == "FP" && iden[j] && bld[j] > 0) { pos = j; break; }
		else if (op == "FP" && pig[j] == "ZP" && iden[j] && bld[j] > 0) { pos = j; break; }
	for (int j = 1; j < i && pos == -1; j++)
		if (op == "MP" && (pig[j] == "FP" && iden[j] || tfpig[j]) && bld[j] > 0) { pos = j; break; }
		else if (op == "ZP" && pig[j] == "FP" && iden[j] && bld[j] > 0) { pos = j; break; }
		else if (op == "FP" && pig[j] == "ZP" && iden[j] && bld[j] > 0) { pos = j; break; }
	return pos;
} // 找到对应种类的猪所要找到的杀的对象,没找到返回-1。
bool find_to_kill(string op, int x) {
	bool can = false;
	string nxt = pig[rpig[x]];
	if (op == "MP" && (nxt == "FP" && iden[rpig[x]] || tfpig[rpig[x]])) can = true;
	else if (op == "ZP" && nxt != "MP" && iden[rpig[x]] && pig[rpig[x]] == "FP") can = true;
	else if (op == "FP" && nxt != "FP" && (rpig[x] == 1 || iden[rpig[x]] && pig[rpig[x]] == "ZP")) can = true;
	return can;
}
void play(int x) { // 模拟
		In(x, 2);
		bool ok = false, use_K = false;
		
		for (int i = 1; i <= tot[x]; i++) {
			if (flag[x][i]) continue;
			char now = c[x][i];
			switch (now) {
				case 'P': peach(x); break;
				case 'K':
					if (find_to_kill(pig[x], x) && (!arm[x] && !use_K || arm[x])) { kill(x, rpig[x]), use_K = true; }
					break;
				case 'F':
					if (pig[x] == "FP") fight(x, 1);
					else if (find(pig[x], x) != -1) fight(x, find(pig[x], x));
					break;
				case 'N': nanzhu(x); break;
				case 'W': wanjian(x); break;
				case 'Z': Arm(x); break;
			}
			if (dead[x]) break;
		}
}
void work() {
	int i = 0;
	while (fpig && bld[1] > 0) {
		i = i % n + 1;
		if (dead[i]) continue;
		else play(i);
	}
}
int main() {
	Read();
	work();
	Print();
	return 0;
}

\(100\)

其实加入了无懈可击,只不过是给代码贴了一点补丁,只要理解了就能很快写出来,推荐用dfs的方式写,个人认为会更好些一些。。。
这里就不贴代码了,请读者自行完成。

posted @ 2024-08-27 13:01  wuzihenb  阅读(148)  评论(0)    收藏  举报