程序设计思维与实践 week9 A - 咕咕东的目录管理器、 B - 东东学打牌、 C - 签到题,独立思考哈

B - 东东学打牌

描述:

最近,东东沉迷于打牌。所以他找到 HRZ、ZJM 等人和他一起打牌。由于人数众多,东东稍微修改了亿下游戏规则:

所有扑克牌只按数字来算大小,忽略花色。
每张扑克牌的大小由一个值表示。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 张牌不同,其值不同。下面依次列举了这手牌的形成规则:

大牌:这手牌不符合下面任一个形成规则。如果 α 和 β 都是大牌,那么定义它们的大小为组成这手牌的 5 张牌的大小总和。

对子:5 张牌中有 2 张牌的值相等。如果 α 和 β 都是对子,比较这个 "对子" 的大小,如果 α 和 β 的 "对子" 大小相等,那么比较剩下 3 张牌的总和。

两对:5 张牌中有两个不同的对子。如果 α 和 β 都是两对,先比较双方较大的那个对子,如果相等,再比较双方较小的那个对子,如果还相等,只能比较 5 张牌中的最后那张牌组不成对子的牌。

三个:5 张牌中有 3 张牌的值相等。如果 α 和 β 都是 "三个",比较这个 "三个" 的大小,如果 α 和 β 的 "三个" 大小相等,那么比较剩下 2 张牌的总和。

三带二:5 张牌中有 3 张牌的值相等,另外 2 张牌值也相等。如果 α 和 β 都是 "三带二",先比较它们的 "三个" 的大小,如果相等,再比较 "对子" 的大小。

炸弹:5 张牌中有 4 张牌的值相等。如果 α 和 β 都是 "炸弹",比较 "炸弹" 的大小,如果相等,比较剩下那张牌的大小。

顺子:5 张牌中形成 x, x+1, x+2, x+3, x+4。如果 α 和 β 都是 "顺子",直接比较两个顺子的最大值。

龙顺:5 张牌分别为 10、J、Q、K、A。

作为一个称职的魔法师,东东得知了全场人手里 5 张牌的情况。他现在要输出一个排行榜。排行榜按照选手们的 “一手牌” 大小进行排序,如果两个选手的牌相等,那么人名字典序小的排在前面。

不料,此时一束宇宙射线扫过,为了躲避宇宙射线,东东慌乱中清空了他脑中的 Cache。请你告诉东东,全场人的排名

输入:

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

输出:

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

样例:

输入:

3
DongDong AAA109
ZJM 678910
Hrz 678910

输出:

Hrz
ZJM
DongDong

想法:

这道题要求对一手牌进行大小比较,我们可以采用结构体重排的方法。
1,结构体player记录了玩家的名字,牌型以及比较过程中可能会用到的第二、三、四比较值。
其中在该结构体中重载小于号,让其满足题目中的大小比较要求:

bool operator < (const player& p) const{//五手牌大小比较规则; 
		if(type!=p.type) return type>p.type;
		if(sec!=p.sec) return sec>p.sec;
		if(third!=p.third) return third>p.third;
		if(four!=p.four) return four>p.four;
		return name<p.name; 
	}

2,其次getchard(string s)函数的功能是将字符串形式的一手牌映射成整数,存到数组中。实现时依次遍历字符串,按照题目中的映射要求将字符映射成整数,其中注意“10”占两位数,遇到“1”需要特殊处理。
3,函数
judge(player temp)
的功能是判断玩家的一手牌属于哪种类型并将玩家结构体加入到vectro容器中。poker数组已经经过排序,在牌型判断过程中注意多种情况记录。

总的来所解题思路为:
构造玩家结构体并重载运算符------转化牌型------判断牌型并记录------排序------输出;

代码:

/*
week9 打牌; 
自定义结构体排序;
结构体数组重排; 
*/ 
#include<iostream>
#include<string.h>
#include<algorithm>
#include<vector>
#include<string>
#include<map>
#include<queue>
#include<stack>
using namespace std;
int n;
int poker[5];
int offset;//双字符差异; 
//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 
void getchard(string s){//记录一手牌; 
	for(int i=0;i<s.size();i++){
		if(s[i]=='A') poker[i-offset]=1;
		else if(s[i]=='1') poker[i-offset]=10,offset++;//"10"
		else if(s[i]>='2'&&s[i]<='9') poker[i-offset]=s[i]-'0';	
		else if(s[i]=='J') poker[i-offset]=11;
		else if(s[i]=='Q') poker[i-offset]=12;
		else if(s[i]=='K') poker[i-offset]=13;
		else if(s[i]=='0') continue; 
	}
}
struct player{//玩家信息; 
	string name;
	int type,sec,third,four;	
	player(){
		type=sec=third=four=0;
	}
	bool operator < (const player& p) const{//五手牌大小比较规则; 
		if(type!=p.type) return type>p.type;
		if(sec!=p.sec) return sec>p.sec;
		if(third!=p.third) return third>p.third;
		if(four!=p.four) return four>p.four;
		return name<p.name; 
	}
};

//玩家结构体; 
vector<player> v;

void judge(player temp){
	    if(poker[0]==1&&poker[1]==10&&poker[2]==11
		&&poker[3]==12&&poker[4]==13){//8龙顺子
			temp.type=8;
			v.push_back(temp);return;
		}			
		if(poker[0]==poker[1]-1&&poker[1]==poker[2]-1
		&&poker[2]==poker[3]-1&&poker[3]==poker[4]-1){//7、顺子
			temp.type=7;//第二比较 
			temp.sec=poker[4];//第一比较 
			v.push_back(temp);return;
		} 
		if(poker[0]==poker[3]){//6、炸弹1 bbbbc
			temp.type=6;
			temp.sec=poker[1];
			temp.third=poker[4];
			v.push_back(temp);return;
		} 
		if(poker[1]==poker[4]){//6、炸弹2 bcccc
			temp.type=6;
			temp.sec=poker[1];
			temp.third=poker[0];
			v.push_back(temp);return;
		}
		if(poker[0]==poker[2]&&poker[3]==poker[4]){//5、三带二1 aaabb
			temp.type=5;
			temp.sec=poker[0];
			temp.third=poker[3];
			v.push_back(temp);return;
		} 
		if(poker[0]==poker[1]&&poker[2]==poker[4]){//5、三带二2 aabbb
			temp.type=5;
			temp.sec=poker[2];
			temp.third=poker[0];
			v.push_back(temp);return;
		}
		if(poker[0]==poker[2]){//4、三个
			temp.type=4;
			temp.sec=poker[0];
			temp.third=poker[3]+poker[4];
			v.push_back(temp);return;
		}
		if(poker[1]==poker[3]){
			temp.type=4;
			temp.sec=poker[1];
			temp.third=poker[0]+poker[4];
			v.push_back(temp);return;
		} 
		if(poker[2]==poker[4]){
			temp.type=4;
			temp.sec=poker[2];
			temp.third=poker[0]+poker[1];
			v.push_back(temp);return;
		}
		
		if(poker[0]==poker[1]){//3、两个对子; 
			if(poker[2]==poker[3]){
				temp.type=3;
				temp.sec=poker[2];
				temp.third=poker[0];
				temp.four=poker[4];
				v.push_back(temp);return;
			}
			if(poker[3]==poker[4]){
				temp.type=3;
				temp.sec=poker[3];
				temp.third=poker[0];
				temp.four=poker[2];
				v.push_back(temp);return;
			}
		}
		if(poker[1]==poker[2]&&poker[3]==poker[4]){
			temp.type=3;
			temp.sec=poker[3];
			temp.third=poker[1];
			temp.four=poker[0];
			v.push_back(temp);return;
		} 
	
		if(poker[0]==poker[1]){//2、一个对子
			temp.type=2;
			temp.sec=poker[0];
			temp.third=poker[2]+poker[3]+poker[4];
			v.push_back(temp);return;
		} 
		if(poker[1]==poker[2]){
			temp.type=2;
			temp.sec=poker[1];
			temp.third=poker[0]+poker[3]+poker[4];
			v.push_back(temp);return;
		}
		if(poker[2]==poker[3]){
			temp.type=2;
			temp.sec=poker[2];
			temp.third=poker[0]+poker[1]+poker[4];
			v.push_back(temp);return;
		}
		if(poker[3]==poker[4]){
			temp.type=2;
			temp.sec=poker[3];
			temp.third=poker[0]+poker[1]+poker[2];
			v.push_back(temp);return;
		}
		//大牌; 
		temp.type=1;
		temp.sec=poker[0]+poker[1]+poker[2]+poker[3]+poker[4];
		v.push_back(temp);return;
} 

int main(){
	while(cin>>n){
		v.clear();	
		for(int i=0;i<n;i++){   
		    offset=0;	
			player temp;
			string s;//临时变量; 
			cin>>temp.name;
			cin>>s;
			getchard(s);//一手牌的类型; 
			sort(poker,poker+5);//整理一手牌; 
			judge(temp);
		}
		sort(v.begin(),v.end());//结构体排序; 
		for(int i=0;i<v.size();i++){
			cout<<v[i].name<<endl;
		}
	}
} 

C - 签到题,独立思考哈

描述:

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

输入:

第一行包含一个整数 x (1 <= x <= 100) 表示公园中长椅的数目
第二行包含一个整数 y (1 <= y <= 1000) 表示有 y 个人来到公园
接下来 x 个整数 a_i (1<=a_i<=100),表示初始时公园长椅上坐着的人数

输出:

输出 mn 和 mx

样例:

输入:

3
7
1
6
1

输出:

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

想法:

这道题的想法比较直接,为了保证人数最多的板凳上的人数最少,我们可以先找出人数最多的板凳,然后将其他的板凳补齐成最多人数板凳的人数。
1,如果剩下的人小于等于0,说明原来的人数最多的板凳所坐的人数就是答案;
2,如果补齐后人数还有剩余,则平均分到每个板凳上,注意上取整。这里的人数定义为float浮点型;

代码:

#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
using namespace std;
float a[120];
float y;
int x;
float mn,mx;
int main()
{
	scanf("%d%f",&x,&y);
	for(int i=0; i<x; i++)
	    scanf("%f",&a[i]);
	sort(a,a+x);
	//都坐一个板凳上; 
	mx=a[x-1]+y;
	//求最小 
	for(int i=0;i<x-1;i++){
		y -= ( a[x-1]-a[i] );//先补齐每一个板凳 
	}
	if( y<=0 ) mn=a[x-1];
	else mn = a[x-1] + ceil(y/x);
	
	cout<<mn<<' '<<mx<<endl;
	return 0;
} 

A - 咕咕东的目录管理器

描述:

在这里插入图片描述在这里插入图片描述在这里插入图片描述时空限制
Time limit 6000 ms
Memory limit 1048576 kB

输入:

输入文件包含多组测试数据,第一行输入一个整数表示测试数据的组数 T (T <= 20);

每组测试数据的第一行输入一个整数表示该组测试数据的命令总数 Q (Q <= 1e5);

每组测试数据的 2 ~ Q+1 行为具体的操作 (MKDIR、RM 操作总数不超过 5000);
面对数据范围你要思考的是他们代表的 “命令” 执行的最大可接受复杂度,只有这样你才能知道你需要设计的是怎样复杂度的系统。

输出:

每组测试数据的输出结果间需要输出一行空行。注意大小写敏感。

样例:

输入:

1
22
MKDIR dira
CD dirb
CD dira
MKDIR a
MKDIR b
MKDIR c
CD ..
MKDIR dirb
CD dirb
MKDIR x
CD ..
MKDIR dirc
CD dirc
MKDIR y
CD ..
SZ
LS
TREE
RM dira
TREE
UNDO
TREE

输出:

OK
ERR
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
9
dira
dirb
dirc
root
dira
a
b
c
dirb
x
dirc
y
OK
root
dirb
x
dirc
y
OK
root
dira
a
b
c
dirb
x
dirc
y

想法:

此题较为硬核,基本是按着助教的实现来写的,路漫漫orz
一,特殊数组C_type枚举了题目中给出的所有可能操作。创建一个目录结构体dir,成员包括目录名称,该节点的父节点,map保存的其孩子节点以及以该目录节点为根的数的大小。其次,因为题中涉及撤销操作,updated记录该目录节点是否被修改过。为了更快获取树的大小,我们在更新树时维护tree_size成员。
二,除了目录结构体,还定义一个操作结构体,成员包括操作的类型,操作的目录。其中对于前三种操作会对目录造成修改,我们将修改的目录节点赋值arg。
在目录结构体中声明题目中所需的关于目录的若干操作。其中1,对于输出目录当前子目录这一操作需要知道目标节点的孩子节点的个数,当子目录个数大于10时分别从头遍历五次,从后遍历5次。2,对于当前节点的前序遍历结果,我们定义三种遍历函数,遍历全部,从前遍历后从后遍历。当以当前节点为根的树的大小大于10时,分别使用后两种遍历方法。
三,对于题目中的撤销方法,我们在进行前三种操作时,将操作存放在commend类型的容器operation中,这样撤销时我们取容器尾部的操作即为最近一次修改。

代码:

/*
week 9目录管理; 
*/
#include<iostream>
#include<vector>
#include<string>
#include<map>
using namespace std;
//七种操作类型: 
const string C_type[] = { "MKDIR","RM","CD","SZ","LS","TREE","UNDO" };
char tmp_str[20];

struct dir {//目录: 
	string name;//名称; 
	dir*parent;//父亲节点 
	map<string, dir*> child;//map保存孩子节点,内含排序 
	int tree_size ;//树大小; 
	
	vector<string> *tendescentants;//数树中的目录,按前序保存; 
	bool updated = true;//更新状态; 
	dir(string n, dir*p) :name(n), parent(p) { tendescentants = new vector<string>; tree_size=1; };
	
	bool addchild(dir*ch) {//增加节点; 
		if (child.find(ch->name) == child.end()) {
			child[ch->name] = ch;
			maintain(+ch->tree_size);
			return true;
		}
		else return false;
	}
	
	dir* mkdir(string arg) {//创建目录; 
		if(child.find(arg)!=child.end())
			return nullptr;
		dir*ch=new dir(arg,this);
		child[arg]=ch;
		maintain(+1);
		return ch;
	}
	
	dir* rm(string arg) {//删除目录 
		auto it = child.find(arg);
		if (it != child.end()) {
			dir*tmp = it->second;
			maintain(-tmp->tree_size);
			child.erase(it);
			return tmp;
		}
		else {
			return nullptr;
		}
	}
	
	dir* getchild(string arg) {//搜索; 
		auto it = child.find(arg);
		if (it == child.end()) {
			return nullptr;
		}
		else return it->second;
	}
	
	dir* cd(string arg) {//切换目录; 
		if (arg == "..") {
			return this->parent;
		}
		return getchild(arg);
	}
	
	void maintain(int p) {//保持; 
		this->updated = true;
		tree_size += p;
		if (parent!=nullptr) parent->maintain(p);
	}
	
	void sz() {//目录大小; 
		printf("%d\n", this->tree_size);
	}
	
	void ls() {//当前目录直接子目录; 
		int num_child = child.size();
		if (num_child == 0) printf("EMPTY\n");
		else if (num_child <= 10) {//子目录小于等于10个 
			for (auto iter = child.begin();iter != child.end();++iter) 
			    cout << iter->first << endl;
		}
		else {//子目录数量多于10个; 
			auto iter = child.begin();
			for (int i = 0;i < 5;++i, ++iter) 
			    cout << iter->first << endl;
			iter = child.end();
			printf("...\n");
			for (int i = 0;i < 5;++i) iter--;
			for (int i = 0;i < 5;++i,++iter) 
			    cout << iter->first << endl;
		}
	}
	
	void tree() {//前序遍历; 
		if (this->tree_size == 1) printf("EMPTY\n");
		else if (this->tree_size <= 10) {//小于等于10个 
			if (this->updated) {
				tendescentants->clear();
				treeAll(tendescentants);
				this->updated = false;
			}
			for (int i=0;i<tree_size;++i){//输出; 
				cout << tendescentants->at(i) << endl;
			}
		}
		else {//多于10个 
			if (this->updated) {
				tendescentants->clear();
				treefir(5, tendescentants);//前 
				treelas(5, tendescentants);//后 
				this->updated = false;
			}
			//输出,注意格式; 
			for (int i = 0;i < 5;i++)	
			    cout << tendescentants->at(i) << endl;
			printf("...\n");
			
			for (int i = 9;i >= 5;--i)   
			    cout << tendescentants->at(i) << endl;
		}
	}
	
	void treeAll(vector<string>* bar) {//整棵树 
		bar->push_back(this->name);
		for (auto &entry : child) {
			entry.second->treeAll(bar);
		}
	}
	
	void treefir(int num, vector<string> *bar) {//从前遍历; 
		bar->push_back(this->name);
		if (--num == 0) return;
		for (auto it = child.begin();it != child.end();++it){
			int cnum = it->second->tree_size;
			if (num <= cnum){
			     it->second->treefir(num, bar);
			     return; 
			}
			else {
				it->second->treefir(cnum, bar);
				num -= cnum;
			}
		}
	}
	void treelas(int num, vector<string>* bar) {//从后遍历; 
		for (auto r_it = child.rbegin();r_it != child.rend();++r_it)
		 {
			int cnum = r_it->second->tree_size;
			if (num <= cnum) {
			     r_it->second->treelas(num, bar);
				 return; 
			}
			else {
				r_it->second->treelas(cnum, bar);
				num -= cnum;
			}
		}
		bar->push_back(this->name);
	}
};

struct command {//操作信息: 
	int type;//操作种类; 
	string arg;//操作的节点 
	dir* op_dir;//操作的目录; 
	command(string cmd) {
		for (int i = 0;i < 7;i++)
			if (cmd == C_type[i]) { type = i;break; }
		if (type < 3){//前三种操作的目标节点; 
		   scanf("%s", tmp_str);
		   arg = tmp_str; 
		}
	}
};

void solve() {
	int n;
	cin>>n;
	//初始操作命令序列; 
	vector<command*> operation;
	//根目录; 
	dir* now = new dir("root", nullptr);
	
	while (n--) {
		scanf("%s", tmp_str);
		command*cmd = new command(tmp_str);
		
		switch (cmd->type) 
		{
			case 0: {//MKDIR  创建; 
				cmd->op_dir = now->mkdir(cmd->arg);
				
				if (cmd->op_dir == nullptr) {
					printf("ERR\n");
					delete cmd;
				}
				else {//成功 
					printf("OK\n");
					operation.push_back(cmd);
				}
				break;
			}
			case 1: {//RM  删除; 
				cmd->op_dir = now->rm(cmd->arg);
				if (cmd->op_dir == nullptr) {
					delete cmd;
					printf("ERR\n");
				}
				else {//成功; 
					printf("OK\n");
					operation.push_back(cmd);
				}
				break;
			}
			case 2: {//CD 进入; 
				dir*tmp = now->cd(cmd->arg);
				if (tmp == nullptr) {//进入失败; 
					delete cmd;
					printf("ERR\n");
				}
				else {
					printf("OK\n");
					cmd->op_dir = now;
					now = tmp;//进入成功切换当前节点; 
					operation.push_back(cmd);
				}
				break;
			}
			//以上三种操作需要记录操作的命令以防撤销; 
			case 3: {//SZ  目录大小; 
				now->sz();delete cmd;break;
			}
			case 4: {//LS  当前目录直接子目录 
				now->ls();delete cmd;break;
			}
			case 5: {// TREE  前序树 
				now->tree();delete cmd;break;
			}
			case 6: {//UNDO  撤销 
				bool success=false;
				while(!success&&!operation.empty()) {
					cmd=operation.back();operation.pop_back();
					switch (cmd->type){
						case 0:success=now->rm(cmd->arg)!=nullptr;break;
						case 1:success=now->addchild(cmd->op_dir);break;
						case 2:now=cmd->op_dir;success=true;break;
					}
				}
				printf(success?"OK\n":"ERR\n");
				delete cmd;
			}
		}
	}
}

int main() {
	int k;
	cin>>k; 
	while (k--) {
		solve();
	}
	return 0;
}

总结

对于题目内容比较多的,可以先构思解题框架;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值