这里写目录标题
201604-3 路径解析
题目
在操作系统中,数据通常以文件的形式存储在文件系统中。文件系统一般采用层次化的组织形式,由目录(或者文件夹)和文件构成,形成一棵树的形状。文件有内容,用于存储数据。目录是容器,可包含文件或其他目录。同一个目录下的所有文件和目录的名字各不相同,不同目录下可以有名字相同的文件或目录。
为了指定文件系统中的某个文件,需要用路径来定位。在类 Unix 系统(Linux、Max OS X、FreeBSD等)中,路径由若*分构成,每个部分是一个目录或者文件的名字,相邻两个部分之间用 / 符号分隔。
有一个特殊的目录被称为根目录,是整个文件系统形成的这棵树的根节点,用一个单独的 / 符号表示。在操作系统中,有当前目录的概念,表示用户目前正在工作的目录。根据出发点可以把路径分为两类:
Ÿ 绝对路径:以 / 符号开头,表示从根目录开始构建的路径。
Ÿ 相对路径:不以 / 符号开头,表示从当前目录开始构建的路径。
例如,有一个文件系统的结构如下图所示。在这个文件系统中,有根目录 / 和其他普通目录 d1、d2、d3、d4,以及文件 f1、f2、f3、f1、f4。其中,两个 f1 是同名文件,但在不同的目录下。
对于 d4 目录下的 f1 文件,可以用绝对路径 /d2/d4/f1 来指定。如果当前目录是 /d2/d3,这个文件也可以用相对路径 …/d4/f1 来指定,这里 … 表示上一级目录(注意,根目录的上一级目录是它本身)。还有 . 表示本目录,例如 /d1/./f1 指定的就是 /d1/f1。注意,如果有多个连续的 / 出现,其效果等同于一个 /,例如 /d1///f1 指定的也是 /d1/f1。
本题会给出一些路径,要求对于每个路径,给出正规化以后的形式。一个路径经过正规化操作后,其指定的文件不变,但是会变成一个不包含 . 和 … 的绝对路径,且不包含连续多个 / 符号。如果一个路径以 / 结尾,那么它代表的一定是一个目录,正规化操作要去掉结尾的 /。若这个路径代表根目录,则正规化操作的结果是 /。若路径为空字符串,则正规化操作的结果是当前目录。
输入格式
第一行包含一个整数 P,表示需要进行正规化操作的路径个数。
第二行包含一个字符串,表示当前目录。
以下 P 行,每行包含一个字符串,表示需要进行正规化操作的路径。
输出格式
共 P 行,每行一个字符串,表示经过正规化操作后的路径,顺序与输入对应。
思路
使用字符串数据类型处理题目,使用getline函数读取每一行的输入路径。
首先需要将输入的相对路径与输入的根目录路径通过“/”连接起来变成绝对路径,之后则需要在新的路径中寻找可以删除或需要修改的元素来生成正确路径。例如在字符串中利用find函数循环寻找连续的“/”即搜索“//”来寻找多余“/”并通过erase函数删除,还有匹配“/./”并删除和删除路径末尾多余“/”的操作。
在用find函数循环匹配到“/…/”即上级目录命令时,如果其位于路径开头则可以直接删除,如果位于路径中间则需要用rfind函数从“/…/”前面开始往前找,找到一个“/”开头的目录后连其与“/…/”一并删除最终得到正规化后的路径
代码
#include<iostream>
#include<string>
using namespace std;
int main()
{
int P;
string root,s;//当前目录和输入的路径
cin>>P>>root;
cin.ignore();
for(int i=0;i<P;i++)
{
string s;
getline(cin, s);
int pos;//定位字符
if(s[0]!='/') //转换为绝对路径形式
s=root+"/"+s;
if(s.size()==0)//空路径处理
s=root;
while((pos = s.find("//"))!=-1) //删除多余“/”
{
int cnt=2;
while(s[pos+cnt]=='/')
cnt++;
s.erase(pos, cnt-1);
}
while((pos=s.find("/./"))!=-1) //删除本目录冗余路径
s.erase(pos, 2);
while((pos=s.find("/../"))!=-1) //处理上级目录
{
if(pos==0)
s.erase(pos,3);
else
{
int p1=s.rfind("/", pos-1);//删除“/../”前路径
s.erase(p1,pos-p1+3);
}
}
if(s.length()>1&&s[s.length()-1]=='/')//删除末尾的“/”
s.erase(s.length()-1);
cout<<s<<endl;
}
return 0;
}
201609-3 炉石传说
题目
《炉石传说:魔兽英雄传》(Hearthstone: Heroes of Warcraft,简称炉石传说)是暴雪娱乐开发的一款集换式卡牌游戏(如下图所示)。游戏在一个战斗棋盘上进行,由两名玩家轮流进行操作,本题所使用的炉石传说游戏的简化规则如下:
- 玩家会控制一些角色,每个角色有自己的生命值和攻击力。当生命值小于等于 0 时,该角色死亡。角色分为英雄和随从。
* 玩家各控制一个英雄,游戏开始时,英雄的生命值为 30,攻击力为 0。当英雄死亡时,游戏结束,英雄未死亡的一方获胜。
* 玩家可在游戏过程中召唤随从。棋盘上每方都有 7 个可用于放置随从的空位,从左到右一字排开,被称为战场。当随从死亡时,它将被从战场上移除。
* 游戏开始后,两位玩家轮流进行操作,每个玩家的连续一组操作称为一个回合。
* 每个回合中,当前玩家可进行零个或者多个以下操作:
1) 召唤随从:玩家召唤一个随从进入战场,随从具有指定的生命值和攻击力。
2) 随从攻击:玩家控制自己的某个随从攻击对手的英雄或者某个随从。
3) 结束回合:玩家声明自己的当前回合结束,游戏将进入对手的回合。该操作一定是一个回合的最后一个操作。
* 当随从攻击时,攻击方和被攻击方会同时对彼此造成等同于自己攻击力的伤害。受到伤害的角色的生命值将会减少,数值等同于受到的伤害。例如,随从 X 的生命值为 HX、攻击力为 AX,随从 Y 的生命值为 HY、攻击力为 AY,如果随从 X 攻击随从 Y,则攻击发生后随从 X 的生命值变为 HX - AY,随从 Y 的生命值变为 HY - AX。攻击发生后,角色的生命值可以为负数。
本题将给出一个游戏的过程,要求编写程序模拟该游戏过程并输出最后的局面。
输入格式
输入第一行是一个整数 n,表示操作的个数。接下来 n 行,每行描述一个操作,格式如下:
…
其中表示操作类型,是一个字符串,共有 3 种:summon表示召唤随从,attack表示随从攻击,end表示结束回合。这 3 种操作的具体格式如下:
* summon :当前玩家在位置召唤一个生命值为、攻击力为的随从。其中是一个 1 到 7 的整数,表示召唤的随从出现在战场上的位置,原来该位置及右边的随从都将顺次向右移动一位。
* attack :当前玩家的角色攻击对方的角色 。是 1 到 7 的整数,表示发起攻击的本方随从编号,是 0 到 7 的整数,表示被攻击的对方角色,0 表示攻击对方英雄,1 到 7 表示攻击对方随从的编号。
* end:当前玩家结束本回合。
注意:随从的编号会随着游戏的进程发生变化,当召唤一个随从时,玩家指定召唤该随从放入战场的位置,此时,原来该位置及右边的所有随从编号都会增加 1。而当一个随从死亡时,它右边的所有随从编号都会减少 1。任意时刻,战场上的随从总是从1开始连续编号。
输出格式
输出共 5 行。
第 1 行包含一个整数,表示这 n 次操作后(以下称为 T 时刻)游戏的胜负结果,1 表示先手玩家获胜,-1 表示后手玩家获胜,0 表示游戏尚未结束,还没有人获胜。
第 2 行包含一个整数,表示 T 时刻先手玩家的英雄的生命值。
第 3 行包含若干个整数,第一个整数 p 表示 T 时刻先手玩家在战场上存活的随从个数,之后 p 个整数,分别表示这些随从在 T 时刻的生命值(按照从左往右的顺序)。
第 4 行和第 5 行与第 2 行和第 3 行类似,只是将玩家从先手玩家换为后手玩家。
思路
使用pair嵌套pair结构来储存游戏角色的信息,外部pair的first储存位置序号,second用pair结构来储存攻击力和血量。
初始化双方的角色信息,使用一个变量player,通过它的值的不同来确定当前属于哪一方的回合,读取到“END”就把player的值从当前状态变为另一种实现角色更换,而“summon”和“attack”操作中将依据当前player值的不同对不同方进行输入的操作。
召唤仆从时,遍历所有的角色信息,如果位置序号大于召唤位置,即位置位于召唤位置及其右侧,则将其位置序号加1,即将其位置右移一位。用一个变量mark标记仆从是否已被召唤,遍历到一个空位置,即位置序号为初始值时将召唤的仆从填在这里,并把mark置于已添加,这样就不会在遍历时遇到第二个空位造成重复添加相同仆从,再把仆从数量遍历anum或bnum加1。
发起攻击时首先遍历双方的所有角色,找到被攻击位置储存信息的索引,然后计算出双方该位置的角色攻击发起后的血量变化,若血量小于0代表死亡,需要将仆从数量anum或bnum减1,遍历该方的角色将其右侧的仆从全部左移1位,并将该索引处的位置序号重置为初始值,最后更新双方角色攻击发起之后的血量。
操作全部结束后通过比较双方英雄的血量判定输赢,使用sort函数将双方仆从按位置序号排序后依次输出完成题目
代码
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
string s;
int n,x,y,z,anum=0,bnum=0,player=1; //anum、bnum为a、b方仆从存活数,player变量用于判定玩家回合
pair<int, pair<int, int> > a[8]; //a方的角色编号,和其攻击力与血量
pair<int, pair<int, int> > b[8]; //b方的角色编号,和其攻击力与血量
int main()
{
a[0]=make_pair(0, make_pair(0, 30)); //初始化a方英雄
b[0]=make_pair(0, make_pair(0, 30)); //初始化b方英雄
for(int i=1;i<8;i++)//初始化a、b的仆从信息
{
a[i].first=-1;
b[i].first=-1;
a[i].second.first=0;
a[i].second.second=0;
b[i].second.first=0;
b[i].second.second=0;
}
cin>>n;//操作数
while(n--)
{
cin>>s;
getchar();
if(s=="summon")//召唤操作
{
cin>>x>>y>>z;
if(player==1)//a方操作,else块为b方
{
int mark=0;//标记是否已添加仆从
for(int i=1;i<8;i++)
{
if(a[i].first>=x) //移动召唤位置及其右边的仆从
a[i].first++;
else if(mark==0&&a[i].first==-1)//在空位召唤新仆从
{
a[i].first=x;
a[i].second.first=y;
a[i].second.second=z;
mark=1;
}
}
anum++;
}
else
{
int mark=0;
for(int i=1;i<8;i++)
{
if(b[i].first>=x)
b[i].first++;
else if(mark==0&&b[i].first==-1)
{
b[i].first=x;
b[i].second.first=y;
b[i].second.second=z;
mark=1;
}
}
bnum++;
}
}
else if(s=="attack")//攻击操作
{
cin>>x>>y;
int al,bl;
if(player==1)//a方操作,else块为b方
{
for(int i=0;i<8;i++)//记录攻击者和被攻击者索引
{
if(a[i].first==x)
al=i;
if(b[i].first==y)
bl=i;
}
int ass=a[al].second.second-b[bl].second.first;//计算a方仆从剩余血量
int bss=b[bl].second.second-a[al].second.first;//计算b方仆从剩余血量
if(ass<=0)//a方仆从死亡
{
anum--;
for(int i=1;i<8;i++)
if(a[i].first>a[al].first)
a[i].first--;
a[al].first=-1;
}
if(bss<=0&&b[bl].first!=0)//b方仆从死亡
{
bnum--;
for(int i=1;i<8;i++)
if(b[i].first>b[bl].first)
b[i].first--;
b[bl].first=-1;
}
//更新血量
a[al].second.second=ass;
b[bl].second.second=bss;
}
else
{
for(int i=0;i<8;i++)
{
if(a[i].first==y)
al=i;
if(b[i].first==x)
bl=i;
}
int ass=a[al].second.second-b[bl].second.first;
int bss=b[bl].second.second-a[al].second.first;
if(ass<=0&&a[al].first!=0)
{
anum--;
for(int i=1;i<8;i++)
if(a[i].first>a[al].first)
a[i].first--;
a[al].first=-1;
}
if(bss<=0)
{
bnum--;
for(int i=1;i<8;i++)
if(b[i].first>b[bl].first)
b[i].first--;
b[bl].first=-1;
}
a[al].second.second=ass;
b[bl].second.second=bss;
}
}
else if(s=="end")//切换玩家
{
if(player==1)
player = 2;
else
player = 1;
}
}
//按位置排序
sort(a+1,a+8);
sort(b+1,b+8);
//判断输赢
if(a[0].second.second>0&& b[0].second.second<=0)
cout<<"1"<<endl;
else if(a[0].second.second<=0&&b[0].second.second>0)
cout<<"-1"<<endl;
else
cout<<"0"<<endl;
//输出英雄仆从状态
cout<<a[0].second.second<<endl;
cout<<anum;
for(int i=1;i<8;i++)
{
if(a[i].second.second>0)
cout<<" "<<a[i].second.second;
}
cout<<endl;
cout<<b[0].second.second<<endl;
cout<<bnum;
for(int i=1;i<8;i++)
{
if(b[i].second.second>0)
cout<<" "<<b[i].second.second;
}
return 0;
}
201809-3 元素选择器
题目
思路
创建结构体node储存元素,分别储存元素的标签、id、层数等级和父亲元素。
首先使用getline函数读取输入的元素信息,通过记录该行元素信息中出现的“.”的个数来定义该元素的层次等级,点数越多等级越低,再确认完等级后将该行字符串中所有的“.”删除,只留下元素的标签、id数据。
使用sstream字符串流来以空格分隔标签和id,提取之后与其等级一起建立一个新的元素节点。使用一个栈parent来确定当前元素的父元素,如果当前元素的等级低于栈顶元素(点数多于)则将当前元素压入栈,且将其父元素赋值为原栈顶元素;如果高于栈顶元素(点数少于)则一直将栈顶元素弹出栈知道栈顶元素的等级高于当前元素位置,再赋值其父元素节点。
使用一个函数方法command来分隔命令中的元素,一个方法judge利用字符匹配来判断元素和命令中的要素是否匹配。输入一个命令后首先调用command进行以空格将命令进行分隔,并将命令要素存进vector结构中,然后遍历所有要素调用judge查看与命令的最后一个要素是否匹配。
使用num记录命令中剩余要素的个数,如果匹配成功且剩余要素不为0即代表需要寻找指定目录下的该元素,则变到当前元素的父节点,并将当前要素变成上一要素的前一个进行新一轮匹配,以此类推直到无法匹配或要素都匹配完毕,后者情况则将元素的序号储存在答案vector中进行输出。
代码
#include <iostream>
#include <fstream>
#include <vector>
#include <stack>
#include <sstream>
using namespace std;
struct node{
string tag,id;//标签和id
int level;//层次
node* parent;//父节点
node(string n,string t,int l){
tag=n;id=t;level=l;parent=0;
}
};
void command(string& s,vector<string>& cmd)//分隔选择器的命令
{
cmd.clear();
string token;
token.clear();
for(int i=0;i<s.size();i++)//以空格分隔命令要素
{
if(s[i]==' ')
{
cmd.push_back(token);
token.clear();
}
else
token+=s[i];
}
cmd.push_back(token);
}
bool judge(node* t,const string& s)//判断是否能和文档内元素匹配
{
if(s[0]=='#')
return s==t->id;
if(s.size()!=t->tag.size())
return false;
for(int i=0;i<s.size();i++)
{
if(tolower(s[i])!=tolower(t->tag[i]))
return false;
}
return true;
}
int main()
{
int n,m;
cin>>n>>m;
vector<node*> nodes;//储存所有元素
nodes.clear();
stack<node*> parents;//储存父节点
while(!parents.empty())
parents.pop();
string s;
getline(cin,s);
while(n--)
{
getline(cin,s);//按行读取文档内容
int level=0;
while(s[level]=='.') //根据“.”的数量判断元素层次等级
level++;
stringstream ss(s.substr(level));//去处所有“.”
string tag,id;
ss>>tag>>id;//分隔输入的标签和id
node* now=new node(tag,id,level);//新建元素信息
if(!parents.empty())//绑定父节点
{
node* p;
while(p=parents.top(),p->level>=level)//找到其上一等级的元素
{
parents.pop();
}
now->parent=p;
}
parents.push(now);
nodes.push_back(now);
}
vector<int> ans;//储存答案序号
vector<string> cmd;//储存命令内容
while(m--)
{
//遍历每一行命令
getline(cin,s);
command(s,cmd);//分隔命令
ans.clear();
for(int i=0;i<nodes.size();i++)//遍历元素
{
int num=cmd.size()-1;//当前命令剩余要素
if(judge(nodes[i],cmd[num]))//从命令的最近一条开始匹配
{
node* t=nodes[i]->parent;
num--;
while(t&&num>=0)//命令还有对指定目录的要求
{
if(judge(t,cmd[num]))
num--;
t=t->parent;
}
if(num==-1) //命令匹配完毕
ans.push_back(i+1);
}
}
//输出答案
cout<<ans.size()<<" ";
for(int i=0;i<ans.size();i++)
cout<<ans[i]<<" ";
cout<<endl;
}
return 0;
}