【SDOI2010】猪国杀 题解(模拟)
前言:嗅到了一丝头秃的味道……
------------------
题目实在太长,变量也很多。建议至少读个三五遍再做题。不要忽略任何细节,不要想当然。(因为真正玩三国杀肯定不像猪一样出牌啊……
总结一些有用的信息:
1.有主猪,忠猪,反猪三种身份。忠猪和反猪可以有多个。
2.游戏规则:反猪要杀死主猪,主猪和忠猪要杀死所有反猪。
3.初始阶段每个人都有四张牌,且初始体力值都是四。
4.每次摸牌都能摸两张牌,且放到自己手牌的最右边。
5.出牌时每次都使用最靠左的能使用的牌。
6.如果没有诸葛连弩,每次最多只能使用一张杀。若有诸葛连弩可以使用无数次杀。
7.当牌被弃置后就与游戏无关。
8.牌的类型:
1.桃:每次使用可以恢复一点体力值,体力值最高能到达体力上限。桃只能对自己使用。当处于自己回合外且自己血量小于等于0时也可以使用。
2.杀:杀的攻击范围为1。可以被闪抵消。可以造成一点伤害。
3.闪:当受到杀的攻击时可以用闪来抵消。
4.决斗:可以对除自己外任意一名角色使用。对方先出杀。谁无杀可以出谁掉一滴血。
5.南蛮入侵:群体伤害。谁不出杀谁掉一滴血。
6.万箭齐发:群体伤害。谁不出闪谁掉一滴血。
7.无懈可击:对决斗使用时,该决斗无效。对5/6使用时,该角色可以不出牌而不受到伤害。对无懈可击使用时,该无懈可击无效。
8.诸葛连弩:装备上可以出无限次杀。
9.伤害来源:谁使用该牌造成伤害,谁就是伤害来源。
10.距离:单向。例如1到2是1,而2到1是n-1。
11.奖励与惩罚:无论谁杀了反贼,都可以摸三张牌。如果主公杀了忠臣,那么主公要弃置所有的牌。
12.几种行为:
1.献殷勤:使用无懈可击挡下南蛮入侵,万箭齐发,决斗;使用无懈可击抵消表敌意。
2.表敌意:对某个角色使用杀/决斗。使用无懈可击抵消献殷勤。
3.跳忠:对某个对主公跳忠或对已经跳忠的猪跳忠的猪跳忠,或对对已经跳反的猪跳反。
4.跳反:与3相反。
13.忠猪不会跳反,反猪也不会跳忠。对所有的猪,能够跳必然跳。
14.对于所有角色:
1.能够使用桃,必然使用。
2.有南蛮入侵/万箭齐发,必然使用。
3.有诸葛连弩必然装上。
4.受到各种威胁时,有牌必然弃置。
5.不会对未标明身份的猪献殷勤/表敌意(包括自己)。
15.特性:
1.主猪:主猪会认为「没有跳身份,且用南猪入侵 / 万箭齐发对自己造成伤害的猪」是类反猪。如果这个猪之后重新跳那么主猪会重新认识这只猪。
2.忠猪:受到来自主猪的决斗时不会使用杀。
3.共性:能献殷勤/表敌意那么一定进行。必须是在自己距离范围内的猪。
----------------------------------
以上加粗的部分是我在写代码时卡住/出bug的地方。希望后人不再犯这些错误QAQ。
整篇代码难度最高的地方就是献殷勤/表敌意和无懈可击。我还是参考了题解才写出来这部分。
先说献殷勤/表敌意。代码中用一个$f(x,y)$函数来表示。对于每只猪,都有一个给定的身份($pid$)和已经跳的身份($id$)。具体如下:
inline int f(int x,int y)//x-->y献殷勤/表敌意。0表敌意,1献殷勤
{
if (a[y].id==-) return -;
if (a[y].id==){if (a[x].pid==) return ;else return -;}
if (a[x].pid==){if (a[y].id==||a[y].id==) return ;else return ;}
if (a[x].pid==){if (a[y].id==||a[y].id==) return ;else return ;}
if (a[x].pid==){if (a[y].id==||a[y].id==) return ;else return ;}
return -;
}
再说无懈可击。无懈可击可以套娃式进行,这是最麻烦的地方。这里要写一个递归再加上各种判断才可以。每次递归后献殷勤/表敌意的关系都会改变。具体看代码:
inline bool J(int x,int y,int v){//无懈可击
if (f(x,y)==v&&have(x,'J')){
update(x,y,);
if (!J(x,y,v^)) return ;
}
for (int i=a[x].nt;i!=x;i=a[i].nt){
if (f(i,y)==v&&have(i,'J')){
update(i,y,);
if (!J(i,y,v^)) return ;
}
}
return ;
}
剩下的地方都不算难,慢慢写就好。最后挂一下我出错的地方:
1.没有注意“类反猪”的定义。
2.没有分清id和pid。
3.忘记了牌的顺序是不能改变的。
4.桃乱用。
5.决斗的伤害来源。
6.递归和循环写死了……
7.各种语法、细节错误数不胜数……
8.链表写炸了
$\cdots$
一点经验:
1.必要的地方要有注释,不然到最后都不清楚自己写的什么。
2.各种操作建议分成多个函数,便于debug。
---------------------------------------------------------------
总之,打这样的大模拟对自己码力的提升也是有很大帮助的。希望后面的同学,鉴之,戒之。
代码:
//一定要注意什么算是类反猪
//分清id和pid
#include<bits/stdc++.h>
using namespace std;
int n,m,L,len;
char s[];//牌堆
struct pig
{
int HP,id,pid,nt,pr,cnt;//血量,表明的身份,给的身份,上一个人,下一个人,计数器
bool equip;//诸葛连弩
char s[];//手牌
int next[],pre[],end;//上一张牌,下一张牌,结尾
}a[];//猪猪
int pig[];//各种身份的数量
inline void ins(int x,char c){
int p=++a[x].cnt;
a[x].next[a[x].end]=p;//改变原来的顺序
a[x].pre[p]=a[x].end;
a[x].end=p;
a[x].s[p]=c;
}
inline char get(){if (L<=m) return s[L++];return s[m];}//获得牌
inline void start(int x){ins(x,get());ins(x,get());}//摸两张牌
inline void del(int x,int pos){//弃牌
a[x].next[a[x].pre[pos]]=a[x].next[pos];
a[x].pre[a[x].next[pos]]=a[x].pre[pos];
if (a[x].end==pos) a[x].end=a[x].pre[pos];
}
inline void clear(){//清空所有牌
a[].next[]=;
a[].end=;
a[].equip=;
}
inline void end(){
if (!pig[]) {printf("FP\n");}
else if (!pig[]) {printf("MP\n");}
for (int i=;i<=n;i++)
{
if (a[i].HP<=)printf("DEAD\n");
else{
for (int j=a[i].next[];j;j=a[i].next[j]) printf("%c ",a[i].s[j]);
printf("\n");
}
}
exit();
}
inline int f(int x,int y)//x-->y献殷勤/表敌意。0表敌意,1献殷勤
{
if (a[y].id==-) return -;
if (a[y].id==){if (a[x].pid==) return ;else return -;}
if (a[x].pid==){if (a[y].id==||a[y].id==) return ;else return ;}
if (a[x].pid==){if (a[y].id==||a[y].id==) return ;else return ;}
if (a[x].pid==){if (a[y].id==||a[y].id==) return ;else return ;}
return -;
}
inline void isend(){
if (pig[]&&pig[]) return;
end();
}//判断游戏是否结束
inline void jl(int x){ins(x,get());ins(x,get());ins(x,get());}
inline void pd(int x,int y){//y是伤害来源,x阵亡
if (a[x].pid==) jl(y);
if (a[x].pid==&&a[y].pid==) clear();
}
inline void P(int x,int pos){
if (a[x].HP==)return;
a[x].HP++;
del(x,pos);
}//桃
inline void isdead(int x,int y){//x是被害人,y是伤害来源
if (a[x].HP>) return;
for (int i=a[x].next[];i;i=a[x].next[i]){
if (a[x].s[i]=='P') P(x,i);
if (a[x].HP>) break;
}
if (a[x].HP<=) {
a[a[x].nt].pr=a[x].pr;a[a[x].pr].nt=a[x].nt;
pig[a[x].pid]--;
isend();
pd(x,y);
}
}
inline void update(int x,int y,int v){
if (a[x].id>=&&a[x].id<=) return;
if (v==) {
if (a[y].pid==) a[x].id=;
return;
}
a[x].id=a[x].pid;
}//只有南蛮入侵和万箭齐发对主公造成伤害时才被认为是类反猪
inline void attack(int x,int y){a[y].HP--;isdead(y,x);}//x对y造成伤害
inline bool have(int x,char c){
for (int i=a[x].next[];i;i=a[x].next[i]){
if (a[x].s[i]==c){
del(x,i);
return ;
}
}
return ;
}
inline bool K(int x,int pos)//杀
{
int y=a[x].nt;
int p=f(x,y);
if (p==-) return ;
if (p==){
del(x,pos);
update(x,y,);
if (!have(y,'D'))attack(x,y);
return ;
}
return ;
}
inline bool J(int x,int y,int v){//无懈可击
if (f(x,y)==v&&have(x,'J')){
update(x,y,);
if (!J(x,y,v^)) return ;
}
for (int i=a[x].nt;i!=x;i=a[i].nt){
if (f(i,y)==v&&have(i,'J')){
update(i,y,);
if (!J(i,y,v^)) return ;
}
}
return ;
}
inline void F(int x,int y)//x对y用决斗,y先出杀
{
if (J(x,y,)) return;
while(){
swap(x,y);
if ((a[x].pid==&&a[y].pid==)||(!have(x,'K'))) {
attack(y,x);
return;
}
}
}
inline void nmrq(int x,int y)
{
if (!J(x,y,)&&!have(y,'K')){
attack(x,y);
update(x,y,);
}
}
inline void wjqf(int x,int y)
{
if (!J(x,y,)&&!have(y,'D')){
attack(x,y);
update(x,y,);
}
}
inline void work(int x)
{
start(x);int flag=;
for (int i=a[x].next[];i;i=a[x].next[i]){
if (a[x].s[i]=='P') P(x,i);
if (a[x].s[i]=='K'&&(!flag||a[x].equip)) if (K(x,i)){
flag++;
i=;
}
if (a[x].s[i]=='F'){
if (a[x].pid==){
del(x,i);
update(x,,);
F(x,);i=;
}
else for(int j=a[x].nt;j!=x;j=a[j].nt){
int p=f(x,j);
if(p==){del(x,i);update(x,j,);F(x,j);i=;break;}
}
if(a[x].HP<=)return;
}
if (a[x].s[i]=='N'){
del(x,i);
for (int j=a[x].nt;j!=x;j=a[j].nt) nmrq(x,j);
i=;
}
if (a[x].s[i]=='W'){
del(x,i);
for (int j=a[x].nt;j!=x;j=a[j].nt) wjqf(x,j);
i=;
}
if (a[x].s[i]=='Z'){
del(x,i);
a[x].equip=;
i=;
}
}
}
inline void solve()
{
scanf("%d%d",&n,&m);//n个猪,m张牌
char c[];int tot;
for (int i=;i<=n;i++)
{
scanf("%s",c+);tot=;
if (c[]=='M') a[i].pid=;
else if (c[]=='Z') a[i].pid=;
else a[i].pid=;
pig[a[i].pid]++;
while(++tot<=) scanf("%s",c+),ins(i,c[]);
}
for (int i=;i<=m;i++) scanf("%s",c+),s[i]=c[];
for (int i=;i<=n;i++)
{
a[i].nt=(i==n)?:i+;
a[i].pr=(i==)?n:i-;
a[i].id=-;
a[i].HP=;
}
L=;a[].id=;isend();
for (int i=;;i=a[i].nt) work(i);
}
int main()
{
solve();
return ;
}
【SDOI2010】猪国杀 题解(模拟)的更多相关文章
- 洛谷P2482 [SDOI2010]猪国杀——题解
猪国杀,模拟题的一颗耀眼的明珠,成长大牛.锻炼码力必写题! 模拟题没什么思维难度.只要按部就班地去做就是.模拟简单在这,难也在这.因为题面巨长,条件巨多,忽疏一点都有可能全盘皆输.故推荐考试时碰见了, ...
- 洛谷 P2482 loj #2885 [SDOI2010]猪国杀 题解【模拟】【贪心】【搜索】
好玩的模拟题. 以后要经常写模拟题鸭 题目描述 游戏背景 <猪国杀>是一种多猪牌类回合制游戏,一共有\(3\)种角色:主猪,忠猪,反猪.每局游戏主猪有且只有\(1\)只,忠猪和反猪可以有多 ...
- Bzoj1972: [Sdoi2010]猪国杀 题解(大模拟+耐心+细心)
猪国杀 - 可读版本 https://mubu.com/doc/2707815814591da4 题目可真长,读题都要一个小时. 这道题很多人都说不可做,耗时间,代码量大,于是,本着不做死就不会死的精 ...
- Luogu2482 [SDOI2010]猪国杀 ---- 模拟
Luogu2482 [SDOI2010]猪国杀 题意 ...... https://www.luogu.org/problemnew/show/P2482 总结 首先说一下代码的构思: 首先确定了所有 ...
- [BZOJ 1972][Sdoi2010]猪国杀
1972: [Sdoi2010]猪国杀 Time Limit: 1 Sec Memory Limit: 64 MBSubmit: 364 Solved: 204[Submit][Status][D ...
- 【BZOJ1972】[SDOI2010] 猪国杀(恶心的大模拟)
点此看题面 大致题意: 让你模拟一个游戏猪国杀的过程. 几大坑点 对于这种模拟题,具体思路就不讲了,就说说有哪些坑点. 题面有锅,反猪是\(FP\). 数据有锅,牌堆中的牌可能不够用,牌堆为空之后需一 ...
- [洛谷P2482][SDOI2010]猪国杀
题目大意:猪国杀,又一道大模拟题 题解:模拟,对于一个没有玩过三国杀的人来说,一堆细节不知道,写的十分吃力 卡点:无数,不想说什么了,这告诉我要多玩游戏 C++ Code: #include < ...
- BZOJ1972:[SDOI2010]猪国杀
我对模拟的理解:https://www.cnblogs.com/AKMer/p/9064018.html 题目传送门:https://www.lydsy.com/JudgeOnline/problem ...
- BZOJ1972:[SDOI2010]猪国杀(模拟)
Description 太长就不贴过来了 Solution 这个题是真的不难写……唯一的难度就在于理解题意上面……感觉这就是个阅读理解题啊…… 而且你三国杀玩的越多可能就越难写因为你无法理解那些猪的思 ...
随机推荐
- day73 bbs项目☞基本功能实现
目录 一.登录功能 二.首页搭建 三.admin后台管理 四.图片防盗链 五.个人站点展示 一.登录功能 views.py 0难度,都是基本操作,要熟悉auth模块的使用 # 登录功能 def log ...
- 数据可视化之DAX篇(三) 认识DAX中的表函数和值函数
https://zhuanlan.zhihu.com/p/64421003 学习 DAX 的过程中,会遇到各种坑,刚开始甚至无法写出一个正确的度量值,总是提示错误.其实很多原因都是不理解 DAX 函数 ...
- 也来谈谈python编码
一.coding:utf-8 让我们先来看一个示例,源码文件是utf-8格式: print('你好 python') 当使用python2执行该程序时会收到一下报错: File "./hel ...
- 图解java方法的简单执行步骤
图解java方法的简单执行步骤 1,找到该方法 2 传入对应的参数 3 执行方法体 4 返回结果
- tomcat内容总结
tomcat的安装以及配置环境变量 1.tomcat的官网下载地址:http://tomcat.apache.org/ tomcat有很多版本,有解压版 和 安装版,还分windows (还分为32位 ...
- 集训作业 洛谷P1866 编号
这个题是个数学题啊. 总体思路不是很难,每个兔子有一个编号,只要不停的看下一个兔子有多少可选编号,再乘上之前的所有可能性就可以算出一共的编号方法. #include<iostream> # ...
- 像写Flutter一样开发Android原生应用
要问到Flutter和Android原生App,在开发是有何区别,编程方式是绕不开的话题.Flutter采用声明式编程,Android原生开发则采用命令式编程. 声明式编程 VS. 命令式编程 我们首 ...
- javascript兼容性:展开运算符 ... 的降级
展开运算符 ... 是一个很好用的ES6新特性,用的好的话,可以节约很多代码. 但是作为ES6特性,它有兼容性问题,而且Babal(在线转码网页)并不会转换展开运算符. 展开运算符大体分为两种用法:展 ...
- iview实战 : 全屏去头去尾的弹窗
<template> <Button @click="modal3 = true" >No title bar</Button> <Mod ...
- 学习jvm(一)--java内存区域
前言 通过学习深入理解java虚拟机的教程,以及自己在网上的查询的资料,做一个对jvm学习过程中的小总结. 本文章内容首先讲解java的内存分布区域,之后讲内存的分配原则以及内存的监控工具.再下来会着 ...