模拟4:week9

1. 目录管理器

题目大意

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
输入表中的命令,输出命令的执行结果

思路
  1. 对于MKDIR,RM,LS,CD操作,这个题需要记录上一级目录与下一级目录之间的关系,所以对于一个确定的节点,设计的数据结构因该能记录所有的子节点,其父节点,并便于进行增删改查操作。
  2. CD操作要求知道当前所在的位置。
  3. 对于undo操作,需要记忆上一次执行成功的操作,以及其涉及到操作对象,并且在恢复的时候,需要的时间的应该尽可能的少。
  4. 对于SZ,TREE操作,这些操作需要展示整棵树的信息,不可能每一次查询,都去遍历一次树结构,需要在节点信息中,直接记录所在子树的信息,并当在子树中出现了增删节点之后,能够更新对应的信息。

具体来说:

  1. 采用map结构记录子节点信息,MKDIR,RM,LS,CD直接对map进行操作增,删,遍历,查找操作。并在MKDIR , RM, CD成功的时候,将所操作的节点编号及操作,保存到一个类似于栈结构容器中,便于undo
  2. SZ 依靠在每次增删子节点的时候,将节点数目向上传递,直到子树中所有的节点的SZ都被更新。
  3. TREE操作依靠每次调用TREE操作的时,对当前节点为跟的子树进行前序遍历,在这一过程中,子树的的子树也会进行前序遍历,子树中的每个节点都会记录自己的前序序列。当下一次查询的节点位于这颗子树中,除非子树中又进行了增删操作,否则可以直接输出记录的前序遍历。
  4. TREE和LS都会涉及到容器的反向遍历
  5. undo可以直接根据提前保存的成功操作,进行恢复(进行原操作的逆过程即可)。
代码
#include<iostream>
#include<vector>
#include<map>
using namespace std;
int T=0,q=0,tot=0;
string cmd="",s="";
int now=0;
struct node{
	string name;
	map<string,int>son;
	int far;
	int sz; 
	bool tag; //lazy更新,查到他了,就更新一次 
	vector<string>pre; //前序,后序 
}nd[100099];
vector<pair<string,pair<string,int>>>st; //记录已经进行的操作,以及操作的点 
void update(int k,int num){
	while(k!=-1){
		nd[k].tag=0; //没有更新 
		nd[k].sz+=num;
		k=nd[k].far;
	}	
}
void mkdir(){
	if(nd[now].son.find(s)!=nd[now].son.end()) {
		cout<<"ERR\n"; return;
	}
	nd[now].son[s]=++tot;
	nd[tot].name=s;
	nd[tot].far=now;
	nd[tot].sz=1;
	nd[tot].far=now;
	nd[tot].tag=0;
	cout<<"OK\n";
	update(now,1); // from his far
	pair<string,int> p(s,tot); 
	st.push_back(make_pair("MKDIR",p));//加入孩子 
}
void rm(){
	if(nd[now].son.find(s)!=nd[now].son.end()){ //zhaodaole
		cout<<"OK"<<"\n";
		int k = nd[now].son[s];
		st.push_back(make_pair("RM",make_pair(s,k)));
		update(now,nd[k].sz*(-1));
		nd[now].son.erase(s);  
	}
	else cout<<"ERR"<<"\n";	
} 
void cd(){
	if(s=="..") {
		if(now==0) cout<<"ERR"<<"\n";
		else  {
			cout<<"OK\n";
			st.push_back(make_pair("CD",make_pair("",now))); // remem src 
			now = nd[now].far;
		}
	}
	else {
		if(nd[now].son.find(s)!=nd[now].son.end()) {
			cout<<"OK"<<"\n";
			st.push_back(make_pair("CD",make_pair("",now))); // remem src 
			now = nd[now].son[s];
		}
		else cout<<"ERR"<<"\n";
	}
}
void getsz(){
	cout<<nd[now].sz<<"\n";
}
void ls(){
  int  ss=nd[now].son.size();
  if(ss==0) cout<<"EMPTY"<<"\n";	
  else if(ss<=10&&ss>=1){
  	for(auto it = nd[now].son.begin();it!=nd[now].son.end();it++)
	    cout<<it->first<<"\n";
  }
  else {
  	auto it = nd[now].son.begin(), it2=nd[now].son.end(); 
  	for(int i=0;i<5;i++,it++) cout<<it->first<<"\n";
  	cout<<"...\n";
    //反向遍历 
  	for(int i=5;i;i--) it2--;
  	for(;it2!=nd[now].son.end();it2++) cout<<it2->first<<"\n"; 
  }
}
void fndmnu(int k){
	nd[k].pre.clear();
	nd[k].pre.push_back(nd[k].name);
	for(auto it =nd[k].son.begin();it!=nd[k].son.end();it++){
		if(!nd[it->second].tag) fndmnu(it->second);
		nd[k].pre.insert(nd[k].pre.end(),nd[it->second].pre.begin(),nd[it->second].pre.end());
	}
	nd[k].tag=1;
}
void tree(){
	if(!nd[now].tag) fndmnu(now); 
	int ss = nd[now].sz;
	if(ss==1) cout<<"EMPTY"<<"\n";
	else if(ss<=10&&ss>=1){
		for(int i=0;i<ss;i++) cout<<nd[now].pre[i]<<"\n";
	}
	else {
	   for(int i=0;i<5;i++) cout<<nd[now].pre[i]<<"\n";
	   cout<<"...\n";
	   for(int i=nd[now].pre.size()-5,j=0;j<5;i++,j++) cout<<nd[now].pre[i]<<"\n";
	} 
}
void undo(){
	if(st.size()==0) cout<<"ERR\n";
	else {
		auto p = st[st.size()-1];
		st.pop_back();
		cout<<"OK"<<"\n";
		if(p.first=="MKDIR"){ // 因为有可能undo 
			int k = p.second.second;
			update(nd[k].far,nd[k].sz*(-1)); 
			nd[nd[k].far].son.erase(p.second.first);
		}
		else if(p.first=="RM"){
			int k = p.second.second;
			update(nd[k].far,nd[k].sz);
			nd[nd[k].far].son[p.second.first] = k;
		}
		else if(p.first=="CD"){
			now = p.second.second;
		}
	}
}
int main()
{
//	freopen("in.txt","r",stdin);
//    std::ios::sync_with_stdio(false);
	scanf("%d",&T);
	while(T--){
		tot=0; 
		scanf("%d",&q);
		now=0;nd[0].name="root";nd[0].sz=1;nd[0].far=-1;nd[0].tag=0;
		st.clear();  //remember  
		for(int i=0;i<q;i++){
			cin>>cmd; 
			if(cmd=="MKDIR") {
				cin>>s;
				mkdir();
			}
			else if(cmd=="RM") {
				cin>>s;
				rm();
			}
			else if(cmd=="CD") {
				cin>>s;
				cd();
			}
			else if(cmd=="SZ") getsz();
			else if(cmd=="LS") ls();      
			else if(cmd=="TREE") tree();
			else if(cmd=="UNDO") undo();
		}
		for(int i=0;i<=tot;i++) {
			if(!nd[i].son.empty()) nd[i].son.clear();
			nd[i].name.clear();
			if(!nd[i].pre.empty()) nd[i].pre.clear();
			nd[i].far = nd[i].tag=0;
		}
	}
	return 0;
}

打牌问题2

题目大意

所有扑克牌只按数字来算大小,忽略花色。,每张扑克牌的大小由一个值表示。A, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K 分别指代 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13。
每个玩家抽得 5 张扑克牌,组成一手牌!(每种扑克牌的张数是无限的)
理所当然地,一手牌是有不同类型,并且有大小之分的。
举个栗子,现在东东的 “一手牌”(记为 α),瑞神的 “一手牌”(记为 β),要么 α > β,要么 α < β,要么 α = β。
那么这两个 “一手牌”,如何进行比较大小呢?首先对于不同类型的一手牌,其值的大小即下面的标号;对于同类型的一手牌,根据组成这手牌的 5 张牌不同,其值不同。下面依次列举了这手牌的形成规则:

  1. 大牌:这手牌不符合下面任一个形成规则。如果 α 和 β 都是大牌,那么定义它们的大小为组成这手牌的 5 张牌的大小总和。
  2. 对子:5 张牌中有 2 张牌的值相等。如果 α 和 β 都是对子,比较这个 “对子” 的大小,如果 α 和 β 的 “对子” 大小相等,那么比较剩下 3 张牌的总和。
  3. 两对:5 张牌中有两个不同的对子。如果 α 和 β 都是两对,先比较双方较大的那个对子,如果相等,再比较双方较小的那个对子,如果还相等,只能比较 5 张牌中的最后那张牌组不成对子的牌。
  4. 三个:5 张牌中有 3 张牌的值相等。如果 α 和 β 都是 “三个”,比较这个 “三个” 的大小,如果 α 和 β 的 “三个” 大小相等,那么比较剩下 2 张牌的总和。
  5. 三带二:5 张牌中有 3 张牌的值相等,另外 2 张牌值也相等。如果 α 和 β 都是 “三带二”,先比较它们的 “三个” 的大小,如果相等,再比较 “对子” 的大小。
  6. 炸弹:5 张牌中有 4 张牌的值相等。如果 α 和 β 都是 “炸弹”,比较 “炸弹” 的大小,如果相等,比较剩下那张牌的大小。
  7. 顺子:5 张牌中形成 x, x+1, x+2, x+3, x+4。如果 α 和 β 都是 “顺子”,直接比较两个顺子的最大值。
  8. 龙顺:5 张牌分别为 10、J、Q、K、A
输入

输入包含多组数据。每组输入开头一个整数 n (1 <= n <= 1e5),表明全场共多少人。
随后是 n 行,每行一个字符串 s1 和 s2 (1 <= |s1|,|s2| <= 10), s1 是对应人的名字,s2 是他手里的牌情况。

输出

对于每组测试数据,输出 n 行,即这次全场人的排名。

思路

此题主要分为两部分,一个是分数的计算,另一个是同分的比较

  1. 排名的计算与之前做过的题目一致,对手牌按由小到大排序之后,从最高分数的情况开始查找,观察手牌是否满足要求,此外,为了便于后续的排序,在确定一手牌的分值之后,还要多记录一些额外的,用于同分比较的信息,如大牌的时候,记录数字之和;对子的时候,记录对子和,以及剩余三张牌的总和。
  2. 同分的比较依靠sort函数来实现比较,若分数不同则直接可以判断;若分数相同,则依靠规则与在分数计算的时候,记录的相关值进行性比较
代码

在将字符转换为数字的时候,读入1的话,其实是10,要跳过后面一位

#include<bits/stdc++.h>
using namespace std;
struct per{
	string name;
	string cards;
	int type;
	int num[20];
	int pos1,pos2;
	int s;
}person[10099];
int tmp[20];
int n=0;
void getPair( per& p) //返回pair的起始位置 
{
	if(p.num[0]==p.num[1]) p.pos1=0;
	else if(p.num[1]==p.num[2]) p.pos1=1;
	else if(p.num[2]==p.num[3]) p.pos1=2; 
	else if(p.num[3]==p.num[4]) p.pos1=3;
	for(int j=0;j<5;j++){
			if(j!=p.pos1&&j!=p.pos1+1)  p.s+=p.num[j];
	    }
}
void getDpair( per&p) //2对 
{
	if(p.num[0]==p.num[1]&&p.num[2]==p.num[3]) p.pos1=0,p.pos2=2,p.s=p.num[4]; 
	else if(p.num[0]==p.num[1]&&p.num[3]==p.num[4]) p.pos1=0,p.pos2=3,p.s=p.num[2];
	else if(p.num[1]==p.num[2]&&p.num[3]==p.num[4]) p.pos1=1,p.pos2=3,p.s=p.num[0];
	if(p.num[p.pos1] <p.num[p.pos2]) swap(p.pos1,p.pos2); //按由大到小的顺序记录,这里要与后边一致 
}
void gettri( per&p) {//三个 
    if(p.num[0]==p.num[2]) p.pos1=0, p.s+=p.num[3]+p.num[4];
	else if(p.num[1]==p.num[3]) p.pos1=1,p.s+=p.num[0]+p.num[4];
	else if(p.num[2]==p.num[4]) p.pos1=2,p.s+=p.num[0]+p.num[1];
} 
void gettriD( per&p){ //3+2 
	if(p.num[0]==p.num[2]&&p.num[3]==p.num[4]) p.pos1=0,p.pos2=3; 
	else if(p.num[0]==p.num[1]&&p.num[2]==p.num[4]) p.pos1=2,p.pos2=0;
}
void getfour( per&p){
	if(p.num[0]==p.num[3]){
		p.pos1=0;
		p.s=p.num[4];
	}
	else if(p.num[1]==p.num[4]){
		p.pos1=1;
		p.s=p.num[0];
	}
}
void findType(int k){
	memset(tmp,0,sizeof tmp);
	string card =person[k].cards;
	for(int i=0,j=0;i<card.size();i++,j++){ //->int 
		if(card[i]=='A') tmp[j]=1;
		else if(card[i]=='1') {
			tmp[j]=10;
			i++;
		}
		else if(card[i]=='J') tmp[j]=11;
		else if(card[i]=='Q') tmp[j]=12;
		else if(card[i]=='K') tmp[j]=13;
		else tmp[j]=card[i]-'0';
	}
	sort(tmp,tmp+5);
	for(int i=0;i<5;i++) person[k].num[i]=tmp[i]; // save the num;
	if(tmp[0]==1&&tmp[1]==10&&tmp[2]==11&&tmp[3]==12&&tmp[4]==13) {
		person[k].type=8; return;
	}
	else if(tmp[0]==tmp[1]-1&&tmp[1]==tmp[2]-1&&tmp[2]==tmp[3]-1&&tmp[3]==tmp[4]-1){
		person[k].type=7; return;
	}
	else if(tmp[0]==tmp[3]||tmp[1]==tmp[4]){ //4个 
		getfour(person[k]);
		person[k].type=6; return;
	}
	else if(tmp[0]==tmp[2]&&tmp[3]==tmp[4] || tmp[0]==tmp[1]&&tmp[2]==tmp[4]){//3+2
	    gettriD(person[k]); 
		person[k].type=5; return;
	}
	else if(tmp[0]==tmp[2]||tmp[1]==tmp[3]||tmp[2]==tmp[4]){//三个 
		gettri(person[k]);
		person[k].type=4; return;
	}
	else if(tmp[0]==tmp[1]&&tmp[2]==tmp[3] ||tmp[0]==tmp[1]&&tmp[3]==tmp[4]||tmp[1]==tmp[2]&&tmp[3]==tmp[4]){
        getDpair(person[k]);
		person[k].type=3; return; //2对 
	}
	else if(tmp[0]==tmp[1]||tmp[1]==tmp[2]||tmp[2]==tmp[3]||tmp[3]==tmp[4]){ //对子 
	    getPair(person[k]);
		person[k].type=2; return;
	}
	else  {
	 person[k].type=1;
	 for(int j=0;j<5;j++) person[k].s+=tmp[j]; 
	} 
}

bool cmp(const per& p, const per & q) {
	if(p.type!=q.type) return p.type>q.type;
	else if(p.type==1){
		if(p.s!=q.s) return p.s>q.s;
	} 
	else if(p.type==2){
	    int a=p.num[p.pos1];
	    int b=q.num[q.pos1];
		if(a!=b) return a>b;
		else if(p.s!=q.s) return p.s>q.s;
	}
	else if(p.type==3){
		if(p.num[p.pos1]!=q.num[q.pos1]) return  p.num[p.pos1]>q.num[q.pos1];
		else if(p.num[p.pos2]!=q.num[q.pos2]) return p.num[p.pos2]> q.num[q.pos2];
		else if(p.s!=q.s) return p.s>q.s;
	}
	else if(p.type==4){
		if(p.num[p.pos1]!=q.num[q.pos1]) return p.num[p.pos1]>q.num[q.pos1];
		else if(p.s!=q.s) return p.s>q.s;
	}
	else if(p.type==5){
		if(p.num[p.pos1]!=q.num[q.pos1]) return p.num[p.pos1]>q.num[q.pos1];
		else if(p.num[p.pos2]!=q.num[q.pos2])  return p.num[p.pos2]>q.num[q.pos2];
	}
	else if(p.type==6){
		if(p.num[p.pos1]!=q.num[q.pos1]) return p.num[p.pos1]>q.num[q.pos1];
		else if(p.s!=q.s) return p.s>q.s;
	}
	else if(p.type==7){
		if(p.num[4]!=q.num[4]) return p.num[4]>q.num[4];
	}
	return p.name<q.name;
}
int main()
{
//	freopen("in.txt","r",stdin);
	while(~scanf("%d",&n))
	{
	  for(int i=0;i<n;i++)
	   {
		  cin>>person[i].name;
		  cin>>person[i].cards;
		  person[i].s=person[i].pos1=person[i].pos2=0;
		  findType(i);
	    }
    	sort(person,person+n,cmp);
	    for(int i=0;i<n;i++) cout<<person[i].name<<"\n";
	}

	return 0;
}

3. 长凳问题

题目大意

滨海公园有 x 条长凳。第 i 个长凳上坐着 a i a_i ai 个人。这时候又有 y 个人将来到公园,他们将选择坐在某些公园中的长凳上,那么当这 y 个人坐下后,记k = 所有椅子上的人数的最大值,那么k可能的最大值mx和最小值mn分别是多少
Output
输出 mn 和 mx

Input Example

3
7
1
6
1

Output Example

6 13

样例解释

最初三张椅子的人数分别为 1 6 1
接下来来了7个人。
可能出现的情况为{1 6 8},{1,7,7},…,{8,6,1}
相对应的k分别为8,7,…,8
其中,状态{1,13,1}的k = 13,为mx
状态{4,6,5}和状态{5,6,4}的k = 6,为mn

思路

mx无疑是max{ai}+k, 而mn的范围在max{ai}~mx之间,mn的满足的条件是,若所有的长凳的人数都是mn,总人数需要超过现有人数,即 m n ∗ x > = k + s u m a i mn*x>=k+sum{ai} mnx>=k+sumai, 且是其中最小的。计算的方法是,遍历max{ai}-mx之间的数,这个题的数据范围较小,不需要二分查找。

代码
#include<bits/stdc++.h>
using namespace std;
int a[10000];
int x,y ; 
int maxk=0,mink=1e8;
int main()
{
//	freopen("in.txt","r",stdin);
	memset(a,0,sizeof a);
	scanf("%d%d",&x,&y);
	int sum=0;
	for(int i=0;i<x;i++) {
		scanf("%d",a+i);
		sum+=a[i];
	}
	sort(a,a+x);
	int tmpmax=a[x-1];
	mink=maxk=tmpmax+y;
    bool f=0;
    for(int i=tmpmax;i<=maxk;i++){
    	int ty=y;
        ty-=i*x-sum;
    	if(ty<=0){
    		mink = min(mink,i);
    		break;
    	}
    	
    }
	printf("%d %d",mink,maxk);
	return 0;
} 

4. 路径解析

题目大意

在文件系统中,根目录,用一个单独的 / 符号表示。在操作系统中,有当前目录的概念,表示用户目前正在工作的目录。根据出发点可以把路径分为两类:
  1. 绝对路径:以 / 符号开头,表示从根目录开始构建的路径。
  2. 相对路径:不以 / 符号开头,表示从当前目录开始构建的路径。
给出一些路径,要求对于每个路径,给出正规化以后的形式。一个路径经过正规化操作后,其指定的文件不变,但是会变成一个不包含 . 和 … 的绝对路径,且不包含连续多个 / 符号。如果一个路径以 / 结尾,那么它代表的一定是一个目录,正规化操作要去掉结尾的 /。若这个路径代表根目录,则正规化操作的结果是 /。若路径为空字符串,则正规化操作的结果是当前目录。

input

第一行包含一个整数 P,表示需要进行正规化操作的路径个数。
  第二行包含一个字符串,表示当前目录。
  以下 P 行,每行包含一个字符串,表示需要进行正规化操作的路径。

output

共 P 行,每行一个字符串,表示经过正规化操作后的路径,顺序与输入对应。

样例输入

7
/d2/d3
/d2/d4/f1
…/d4/f1
/d1/./f1
/d1///f1
/d1/
///
/d1/…/…/d2

样例输出

/d2/d4/f1
/d2/d4/f1
/d1/f1
/d1/f1
/d1
/
/d2

思路

此题首先将当前目录按照‘/’进行切分,存取对应的目录名称,将其放入一个类似于栈的结构,并维护一个栈顶指针now。之后,若输入是相对路径,也是按照‘/’进行切分,对于切分出来的内容:(使用一个vector< s t r i n g string string> ans 来保存)
1. 若是…, 当ans数组为空的时候,则now–, 直到now<0,即:now>=0?–now:-1; 若ans不为空,则弹出尾元素。
2. 若是., 则不需要进行任何处理
3. 若是非空字符串,则在保证和前一个保存的字符串不重名的前提下,保存这个字符串;否则不保存
当输出相对路径的时候,若now<0,则不再管当前路径,直接输出解析之后保存的路径,否则,需要先输出0~now之间的,当前路径,再输出保存的路径。

若是绝对路径,在处理…的时候,只需考虑ans的尾元素弹出即可,不用考虑当前路径;输出的时候,也不在考虑当前路径,其余和相对路径一样。

代码
#include<bits/stdc++.h>
using namespace std;
int P=0,now=0;
string cur="",path="",p2=""; 
vector<string>st,ans,ans2;
void splitStr(){
	string t="";
    for(int i=0;i<cur.size();i++){
    	if(cur[i]=='/'){
    		if(!t.empty()) st.push_back(t);
    		t.clear();
    	} 
    	else t+=cur[i];
    }
    if(!t.empty()) st.push_back(t);
}
void getans1(string & a){
     if(a=="..") { //回到上一级文件 
		if(ans.size()>0) ans.pop_back(); 
		else if(st.size()!=0) now>=0?--now:-1;  
     }
    else if(a=="."){ } 
    else if(!a.empty()) {
	   if(ans.empty())  ans.push_back(a);
       if(ans[ans.size()-1]!=a) ans.push_back(a);
	 } 
}
void getans2(string &a){
	if(a=="..") { //回到上一级文件 
		if(ans.size()>0) ans.pop_back(); 
     }
//    else if(a=="."){} 
     else if(!a.empty()&&a!=".") {
	 if(ans.empty()) ans.push_back(a);
	 else if(ans[ans.size()-1]!=a) ans.push_back(a); //不能和之前重复 	  
	 } 
}
void solve()
{
    if(path.empty()) {
    	cout<<cur<<"\n";
    	return; //这里注意 
    }
    now = st.size()-1;
    string a="";
	if(path[0]!='/'){ //相对路径 
        p2 = path;  
		for(int i=0;i<p2.size();i++){
			if(p2[i]!='/') a+=p2[i];
		    else	getans1(a),	a.clear();
		   }
		   getans1(a);
		   ans2.clear();
		   cout<<"/"; //根目录
		  for(int i=0;i<=now;i++) ans2.push_back(st[i]);
		  ans2.insert(ans2.end(),ans.begin(),ans.end());
          for(int i=0;i<ans2.size();i++){
			cout<<ans2[i];
			if(i<ans2.size()-1) cout<<"/";
		    } 
             cout<<"\n";
     }
     else if(path[0]=='/'){ //绝对路径
	     p2 = path.substr(1,path.size());
		 for(int i=0;i<p2.size();i++){
		 	if(p2[i]!='/') a+=p2[i];
		 	else getans2(a),a.clear();
		 }
		 getans2(a);
		  cout<<"/"; //根目录
           for(int i=0;i<ans.size();i++){
			cout<<ans[i];
			if(i<ans.size()-1) cout<<"/";
	     	} 
           cout<<"\n"; 
	  } 
} 
int main()
{
//	freopen("in.txt","r",stdin);
	scanf("%d",&P);
	st.clear();
	getchar();
    getline(cin,cur);
	splitStr(); 
	while(P--){
		ans.clear();
		path.clear();
		p2.clear(); 
		getline(cin,path);
		solve();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值