[SDOI2010] 猪国杀

猪国杀

前言

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

题意

题目传送门

游戏目的

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

游戏过程

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

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

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

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

牌的种类

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

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

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

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

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

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

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

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

特殊事件及概念解释

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

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

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

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

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

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

几种行为

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

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

行动准则

共性
  • 每个角色如果手里有桃且生命值未满,那么必然吃掉;
  • 有南猪入侵、万箭齐发、必然使用;有装备必然装上;
  • 受到杀时,有闪必然弃置;
  • 响应南猪入侵或者万箭齐发时候,有杀 / 闪必然弃置;
  • 不会对未表明身份的猪献殷勤(包括自己)。
特性
  • 主猪:
    • 主猪会认为「没有跳身份,且用南猪入侵 / 万箭齐发对自己造成伤害的猪」是反猪(没伤害到不算,注意类反猪并没有表明身份),如果之后跳了,那么主猪会重新认识这只猪;
    • 对于每种表敌意的方式,对逆时针方向能够执行到的第一只类反猪或者已跳反猪表;如果没有,那么就不表敌意;
    • 决斗时会不遗余力弃置杀;
    • 如果能对已经跳忠的猪或自己献殷勤,那么一定献;如果能够对已经跳反的猪表敌意,那么一定表。
  • 忠猪:
    • 对于每种表敌意的方式,对「逆时针方向能够执行到的第一只已经跳反的猪」表,如果没有,那么就不表敌意;
    • 决斗时,如果对方是主猪,那么不会弃置杀,否则,会不遗余力弃置杀;
    • 如果有机会对主猪或者已经跳忠的猪献殷勤,那么一定献。
  • 反猪:
    • 对于每种表敌意的方式,如果有机会则对主猪表,否则,对「逆时针方向能够执行到的第一只已经跳忠的猪」表,如果没有,那么就不表敌意;
    • 决斗时会不遗余力弃置杀
    • 如果有机会对已经跳反的猪献殷勤,那么一定献

做法

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

其中这题的部分分为:

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

10 10 10 分,没有锦囊牌

辅助数组

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

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

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

  1. bool tfpig[i]:表示第 i i i 只猪是否为类反猪。
  2. bool iden[i]:表示第 i i i 只猪是否跳身份。
  3. bool flag[i][j]:表示第 i i i 只猪的第 j j j 张卡牌是否被用。
辅助函数
  1. count(x, t):表示第 i i i 只猪的卡牌中有几张为 t t 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 4 4,而 bld_back 是当前这头猪在血量小于等于 0 0 0 时,用桃将其血量回到 1 1 1
5. Dead(x, y):表示 x x x y y y 弄(可能是南猪入侵、万箭齐发、决斗或者杀)死后的处理:

  • p i g [ y ] pig[y] pig[y] 为主猪,直接输出
  • p i g [ y ] pig[y] pig[y] 为反猪,若反猪数量为 0 0 0,直接输出,否则 x x x 3 3 3 张牌。
  • p i g [ x ] pig[x] 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 i i 加入两张牌,thrw(i):表示将 i 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 30 30 分,没有无懈可击

决斗

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

  • x K ≥ y K xK \ge yK xKyK:则 y y y 扣一滴血。
  • x K < y K xK < yK xK<yK:则 x x 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 i 头猪要判断下是否死亡。假设枚举到了第 i i i 只猪:

  • K K K:啥事都不用干。
  • 没有 K K K:扣一滴血,若 p i g [ i ] pig[i] 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 100 100

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

  • 12
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值