当前位置: 首页 > news >正文

UVa 174 Strategy

题目分析

本题是一个博弈策略模拟问题。两个玩家同时选择TRADE\texttt{TRADE}TRADECHEAT\texttt{CHEAT}CHEAT,根据组合结果获得不同分数:

  • 双方都TRADE\texttt{TRADE}TRADE:各得111
  • 一方TRADE\texttt{TRADE}TRADE,另一方CHEAT\texttt{CHEAT}CHEATTRADE\texttt{TRADE}TRADE方得−2-22分,CHEAT\texttt{CHEAT}CHEAT方得222
  • 双方都CHEAT\texttt{CHEAT}CHEAT:各得−1-11

每个策略由一个简单的程序描述,程序支持条件判断和基本命令。条件可以访问最近两次交锋的历史记录(自己的或对方的)。

题目要求读入最多101010个策略程序,让每个策略与其它每个策略对战101010次(不与自己对战),输出每个策略的最终总分。

解题思路

1. 解析策略程序

程序遵循给定的文法规则:

  • 基本命令:TRADE\texttt{TRADE}TRADECHEAT\texttt{CHEAT}CHEAT
  • 条件语句:IF condition THEN statement ELSE statement\texttt{IF condition THEN statement ELSE statement}IF condition THEN statement ELSE statement
  • 条件:可以组合AND\texttt{AND}AND/OR\texttt{OR}OR,比较MY LAST1\texttt{MY LAST1}MY LAST1YOUR LAST2\texttt{YOUR LAST2}YOUR LAST2等与TRADE\texttt{TRADE}TRADECHEAT\texttt{CHEAT}CHEATNULL\texttt{NULL}NULL是否相等或不等于

解析采用递归下降方法。每个节点可能是命令节点(叶子)或条件节点(内部节点)。对于条件节点,提取条件字符串,并递归解析THEN\texttt{THEN}THENELSE\texttt{ELSE}ELSE分支。

2. 条件求值

条件表达式支持:

  • 原子条件:如MY LAST1 = CHEAT\texttt{MY LAST1 = CHEAT}MY LAST1 = CHEAT
  • 复合条件:使用AND\texttt{AND}ANDOR\texttt{OR}OR连接

求值时,根据当前记忆的历史记录(最多最近两次交锋)判断条件真伪。注意处理NULL\texttt{NULL}NULL表示“未发生交锋”的情况。

3. 对战模拟

对于每对不同的策略iiijjj

  • 初始化双方的历史记录为空
  • 重复101010次:
    • 根据各自的历史记录(注意角色互换:对iii来说是 “MY\texttt{MY}MY” 的历史是它之前对jjj的动作,“YOUR\texttt{YOUR}YOUR” 是jjj的动作;对jjj则相反)
    • 解析得到本轮动作
    • 更新历史记录
    • 根据规则计算分数累加到双方总分

4. 输出

每个策略的总分右对齐,宽度为333

代码实现

// Strategy// UVa ID: 174// Verdict: Accepted// Submission Date: 2016-03-07// UVa Run Time: 0.000s//// 版权所有(C)2016,邱秋。metaphysis # yeah dot net#include<bits/stdc++.h>usingnamespacestd;// 定义动作类型常量constintTRADE=0;// 合作constintCHEAT=1;// 欺骗constintCONDITION=2;// 条件节点constintNONE=3;// 表示NULL,即未发生交锋// 语法树节点结构structnode{intcategory;// 节点类型:TRADE / CHEAT / CONDITIONstring conditions;// 条件字符串(仅当 category == CONDITION 时有效)node*leftChild,*rightChild;// 条件节点的 THEN 和 ELSE 分支};vector<string>programs;// 存储所有程序字符串vector<node*>nodes;// 存储所有解析后的语法树根节点// 去除程序字符串末尾空格和制表符,并移除末尾的句点stringspaceOut(string line){for(inti=line.length()-1;i>=0;i--)if(line[i]==' '||line[i]=='\t')line.erase(line.begin()+i);line.erase(line.end()-1);// 移除句点returnline;}// 全局解析状态string strategy;// 当前正在解析的程序intidx=0;// 当前解析位置// 递归解析策略程序,构建语法树voidparseStrategy(node*parent){if(idx>=strategy.length())return;if(strategy[idx]=='T'){// TRADE 命令parent->category=TRADE;parent->leftChild=NULL;parent->rightChild=NULL;idx+=5;// 跳过 "TRADE"}elseif(strategy[idx]=='C'){// CHEAT 命令parent->category=CHEAT;parent->leftChild=NULL;parent->rightChild=NULL;idx+=5;// 跳过 "CHEAT"}elseif(strategy[idx]=='I'){// IF 条件语句parent->category=CONDITION;idx+=2;// 跳过 "IF"// 找到 THEN 的位置,提取条件字符串intthenIndex=strategy.find("THEN",idx);parent->conditions=strategy.substr(idx,thenIndex-idx);idx=thenIndex+4;// 跳过 "THEN"// 解析 THEN 分支node*leftNode=newnode;parseStrategy(leftNode);parent->leftChild=leftNode;idx+=4;// 跳过 "ELSE"// 解析 ELSE 分支node*rightNode=newnode;parseStrategy(rightNode);parent->rightChild=rightNode;}}// 求值一个原子条件,如 "MY LAST1 = CHEAT"// part: 条件字符串,如 "MY LAST1 = CHEAT"// my: 自己的历史动作序列(最近的在末尾)// your: 对方的历史动作序列boolevaluatePart(string part,vector<int>&my,vector<int>&your){boolisEqual=true;// 默认是比较相等intop=part.find("=");if(op==part.npos){// 找不到等号,则尝试不等号op=part.find("#");isEqual=false;// 不等比较}// 获取比较的右操作数intright=NONE;if(part[op+1]=='T')right=TRADE;if(part[op+1]=='C')right=CHEAT;// 获取 LAST1 或 LAST2 中的数字intidx=part[op-1]-'0';if(part[0]=='M'){// MY LAST...if(idx>my.size())// 历史记录不足,视为 NULLreturnisEqual?NONE==right:NONE!=right;elsereturnisEqual?my[my.size()-idx]==right:my[my.size()-idx]!=right;}else{// YOUR LAST...if(idx>your.size())returnisEqual?NONE==right:NONE!=right;elsereturnisEqual?your[your.size()-idx]==right:your[your.size()-idx]!=right;}}// 递归求值复合条件(支持 AND / OR)boolevaluate(string conditions,vector<int>&my,vector<int>&your){intorIndex=conditions.find("OR",0);intandIndex=conditions.find("AND",0);// 原子条件,直接求值if(orIndex==conditions.npos&&andIndex==conditions.npos)returnevaluatePart(conditions,my,your);intopIndex,nextIndex;boolisAnd=false;// 确定优先级:先找位置靠前的操作符if(orIndex==conditions.npos){opIndex=andIndex;nextIndex=andIndex+3;isAnd=true;}elseif(andIndex==conditions.npos){opIndex=orIndex;nextIndex=orIndex+2;}elseif(orIndex<andIndex){opIndex=orIndex;nextIndex=orIndex+2;}else{opIndex=andIndex;nextIndex=andIndex+3;isAnd=true;}// 递归求值左右部分boolleftPart=evaluatePart(conditions.substr(0,opIndex),my,your);boolrightPart=evaluate(conditions.substr(nextIndex),my,your);returnisAnd?(leftPart&&rightPart):(leftPart||rightPart);}// 根据当前历史记录,执行策略树得到本轮动作intgetStrategy(node*parent,vector<int>&my,vector<int>&your){if(parent->category==TRADE||parent->category==CHEAT)returnparent->category;// 条件节点:根据条件真假选择分支returnevaluate(parent->conditions,my,your)?getStrategy(parent->leftChild,my,your):getStrategy(parent->rightChild,my,your);}// 模拟一局对战,更新双方历史记录和分数vector<int>myMemory,yourMemory;// 临时存储当前对战的历史intscores=0;// 当前策略的总分voidexecute(inti,intj){// 获取双方本轮动作intmy=getStrategy(nodes[i],myMemory,yourMemory);intyour=getStrategy(nodes[j],yourMemory,myMemory);// 更新历史记录myMemory.push_back(my);yourMemory.push_back(your);// 根据规则计算分数if(my==TRADE&&your==TRADE)scores++;if(my==CHEAT&&your==CHEAT)scores--;if(my==TRADE&&your==CHEAT)scores-=2;if(my==CHEAT&&your==TRADE)scores+=2;}// 主对战逻辑voidplay(){// 解析所有程序for(inti=0;i<programs.size();i++){idx=0;strategy=programs[i];node*root=newnode;parseStrategy(root);nodes.push_back(root);}// 对每个策略 ifor(inti=0;i<nodes.size();i++){scores=0;// 与除自己外的每个策略 j 对战for(intj=0;j<nodes.size();j++){if(i==j)continue;myMemory.clear();yourMemory.clear();// 对战 10 次for(intk=1;k<=10;k++)execute(i,j);}// 输出最终分数,右对齐宽度 3cout<<right<<setw(3)<<scores<<"\n";}}intmain(intargc,char*argv[]){cin.tie(0);cout.sync_with_stdio(false);string line,program;while(getline(cin,line),line!="#"){program+=line;// 遇到句点表示一个程序结束if(line.find('.')!=line.npos){programs.push_back(spaceOut(program));program.clear();}}play();return0;}

要点总结

  1. 语法解析:采用递归下降法,每个IF\texttt{IF}IF语句对应一个内部节点,左右子节点分别对应THEN\texttt{THEN}THENELSE\texttt{ELSE}ELSE分支。
  2. 历史记录管理:使用两个向量分别存储自己和对方的历史动作,方便按LAST1\texttt{LAST1}LAST1LAST2\texttt{LAST2}LAST2索引。
  3. 条件求值:支持AND\texttt{AND}AND/OR\texttt{OR}OR的短路求值逻辑,正确处理NULL\texttt{NULL}NULL情况。
  4. 对战模拟:注意iiijjj时,双方历史记录的角色是互换的。
http://www.jsqmd.com/news/739836/

相关文章:

  • 动态3D重建技术COM4D:单目视频实现高质量4D建模
  • CT影像三维重建第一步:手把手教你理解DICOM的Patient Position与图像方向
  • 从`[1]`到`(Author, 2023)`:详解如何在LaTeX中为Elsevier期刊定制参考文献引用样式(以EJOR为例)
  • 终极视频翻译配音工具:PyVideoTrans完整指南与实战教程
  • WPS-Zotero:打破平台壁垒的学术写作新范式
  • DeepSeek-V4(Pro|Flash)架构革命与国产大模型的高光时刻——超长上下文、双轴稀疏架构、万亿参数、开源免费、华为昇腾等国产芯片全栈适配
  • 从零搭建汽车CAN网络:手把手教你用CANdb++ Admin完成数据库管理与分析
  • STM32小车仿真避坑指南:从12V降压到TB6612驱动,我的Proteus电源与电机配置心得
  • 5秒快速转换:如何将B站缓存视频永久保存为MP4格式
  • 基于Node.js的本地网络请求过滤工具:规则引擎与SNI嗅探实践
  • 用PN532和一部安卓手机,5分钟复制你家老旧门禁卡(保姆级避坑教程)
  • Linux多线程编程完全指南:线程同步、互斥锁与生产者消费者模型
  • 3步完成Amlogic电视盒子Armbian系统安装:从闲置硬件到高效服务器
  • 如何彻底告别网盘限速:LinkSwift八大网盘直链下载助手终极指南
  • TrendForge 每日精选 9 个热门开源项目,mattpocock/skills 新增 3645 星成“今日之星”
  • 机器人通用化训练:世界基础模型与合成数据技术突破
  • 最短路径-Dijkstra算法(迪杰斯特拉算法)
  • 向量搜索技术解析:从原理到工程实践
  • FPGA在智能电网中的实时处理与可靠性设计
  • 2026天津专业防水公司TOP5推荐:卫生间、外墙、楼顶、地下室渗漏专业公司推荐(2026年5月天津最新深度调研方案) - 防水百科
  • 如何使用face-api.js快速实现人脸识别:7个实用技巧与解决方案
  • 别再死记硬背了!用ENSP模拟器一步步拆解华为MSTP、VRRP、DHCP中继的联动原理与配置
  • 手把手教你用libexpat解析XML配置文件:一个C语言嵌入式项目的完整实战
  • 告别双系统折腾:用VMware+Ubuntu+Miniconda打造你的轻量级PyTorch学习环境
  • 异步强化学习框架优化LLM训练效率
  • 基于Whisper的音频转录实战:从架构设计到生产部署
  • 2026年3月靠谱的日本留学就业品牌推荐,EJU培训/日本留学签证办理/日语培训,日本留学就业中心推荐口碑分析 - 品牌推荐师
  • AI智能体如何成为基础设施炼金术士:从IaC到生产就绪的自动化实践
  • 高通SM6225 GKI 2.0编译效率提升指南:巧用SKIP_MRPROPER与模块化编译
  • OrgChart.js终极指南:5分钟快速创建专业组织结构图