程序设计思维与实践 Week9 作业

C - 滨海公园

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


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

Output
输出 mn 和 mx

Input Example
3
7
1
6
1

Output Example
6 13


思路: 贪婪思想,最大值mx就所有人扎堆到本来就多的。最小值mn尽量分散开,去人少的地方。
输入原本每个凳子人数的时候记录下最大值max,max+之后来的就是mx;
然后求最小,先求一下每个凳子与人数最多凳子之差的和sum,如果新来的小于sum,mn就是max,否则将剩下的人分散到各个凳子


#include <iostream>
using namespace std;

int v[105];
int main()
{
    int x, y, mx, mn;
    cin >> x >> y;
    int max = 0, sum = 0;
    for (int i = 1; i <= x; i++)
    {
        cin >> v[i];
        max = v[i] > max ? v[i] : max;
    }
    mx = max + y;
    for (int i = 1; i <= x; i++)
        sum += (max - v[i]);
    if (y <= sum)
        mn = max;
    else
        mn = max + (y - sum) / x + ((y - sum) % x > 0);
    cout << mn << " " << mx;
}

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


思路:一个比较大小的问题,关键在于如何分配value。value可以是个int。
牌有字母有数字,先统一一下,定义一个结构体表示一张牌

struct card
{
	int value;//A为1,JQK分别为11 12 13
	operator int()
	{
		return value;
	}
};

定义结构体person

struct person
{
	string name;
	card cards[5];
	int value;//一手牌的大小
};

先输入,输入牌的时候转换为我想要的value(A–1,J–11,Q–12,K–13),
输入为1的时候说明遇到了10,多读一位即可。
然后给每套牌赋value,简单说一下判断方法
先给5张牌排序,这样方便后面判断,这步比较关键
然后开始依次判断,大牌型优先级高
龙牌:找一下牌中有没有A,有的话变成14,然后判断是不是顺子
顺子:已经排好序了,比较一下相邻相等就行了
炸弹:看相邻四个是不是相等
三个和两个差不多,说说两个的
判断对子的方法是遍历牌,如果这张和下张相等则是对子,然后i++,
循环范围是第一张到倒数第二张,最后一张要特殊处理一下

比较分多个层级,可以把给每个层级分配位数,比如是顺子value就加个7 * 106,然后看看顺子的第一张,比如是A,那就加上1 * 104,之所以用到
六次方,是因为最多分三层比较,每层给两位,因为最大k牌value是13

最后一步排序即可


#include <iostream>
#include <sstream>
#include <algorithm>
using namespace std;
const int MAXN = 10000 + 5;

struct card
{
	int value;
	operator int()
	{
		return value;
	}
};
struct person
{
	string name;
	card cards[5];
	int value;
};
person ps[MAXN];
int indx;


bool shunzi(person& p)
{
	if ((p.cards[0] + 1 == p.cards[1]) && (p.cards[1] + 1 == p.cards[2]) &&
		(p.cards[2] + 1 == p.cards[3]) && (p.cards[3] + 1 == p.cards[4]))
	{
		p.value = 7000000 + p.cards[0] * 10000;
		return true;
	}
	return false;
}
bool longshun(person& p)
{
	for (int i = 0; i < 5; i++)
		if (p.cards[i] == 1)
		{
			p.cards[i].value = 14;
			sort(p.cards, p.cards + 5, [](card l, card r) {
				return l.value < r.value;
			});
			if (shunzi(p))
			{
				p.value = 8000000;
				p.cards[4].value = 1;
				return true;
			}
			p.cards[4].value = 1;
			sort(p.cards, p.cards + 5, [](card l, card r) {
				return l.value < r.value;
			});
			break;
		}
	return false;
}
bool boom(person& p)
{
	if (p.cards[0] == p.cards[3])
	{
		p.value = 6000000 + p.cards[2] * 10000 + p.cards[4] * 100;
		return true;
	}
	if (p.cards[1] == p.cards[4])
	{
		p.value = 6000000 + p.cards[2] *10000 + p.cards[0] *100;
		return true;
	}
	return false;
}
bool san(person& p)
{
	if (p.cards[0] == p.cards[2])
	{
		if (p.cards[3] == p.cards[4])
			p.value = 5000000 + p.cards[0] * 10000 + p.cards[3] * 100;
		else p.value = 4000000 + p.cards[0] * 10000 + p.cards[3] * 100 + p.cards[4] * 100;
		return true;
	}
	if (p.cards[2] == p.cards[4])
	{
		if (p.cards[0] == p.cards[1])
			p.value = 5000000 + p.cards[2] * 10000 + p.cards[0] * 100;
		else p.value = 4000000 + p.cards[2] * 10000 + p.cards[0] * 100 + p.cards[1] * 100;
		return true;
	}
	if (p.cards[1] == p.cards[3])
	{
	 p.value = 4000000 + p.cards[1] * 10000 + p.cards[0] * 100 + p.cards[4] * 100;
	return true;
	}
	return false;
}
bool dui(person& p)
{
	int v1 = 0, v2 = 0, sum = 0;
	for (int i = 0; i <= 3; i++)
	{
		if (p.cards[i] == p.cards[i + 1])
		{
			v2 = v1 == 0 ? 0 : p.cards[i];
			v1 = v1 == 0 ? p.cards[i] : v1;
			i++;
		}
		else sum += p.cards[i];
	}
	if (p.cards[3] != p.cards[4]) sum += p.cards[4];

	int hi = max(v1, v2), lo = v1 + v2 - hi;
	if (v1)
	{
		p.value = 2000000 + hi * 10000 + lo * 100 + sum + (v2 > 0) * 1000000;
		return true;
	}
	return false;
}
void dapai(person& p)
{
	for (int i = 0; i < 5; i++) p.value += p.cards[i];
	return;
}
void judge(person& p)
{
	sort(p.cards, p.cards + 5, [](card l, card r) {
		return l.value < r.value;
	});
	if (longshun(p)) return;
	if (shunzi(p)) return;
	if (boom(p)) return;
	if (san(p)) return;
	if (dui(p)) return;
	else dapai(p);
	return;
}
int main()
{
	int n;
	while (cin >> n)
	{
		indx = 0;
		for (int i = 0; i < n; i++)
		{
			person p;
			string name, ca; cin >> p.name >> ca;
			stringstream ss(ca);
			char c;
			int x = 0;
			while (ss >> c)
			{
				if (c == '1')
				{
					ss >> c;
					p.cards[x++].value = 10;
				}
				else if (c == 'A') p.cards[x++].value = 1;
				else if (c == 'J') p.cards[x++].value = 11;
				else if (c == 'Q') p.cards[x++].value = 12;
				else if (c == 'K') p.cards[x++].value = 13;
				else p.cards[x++].value = c - '0';
			}
			p.value = 0;
			ps[indx++] = p;
			judge(ps[indx - 1]);
		}
		sort(ps, ps + indx, [](const person& l, const person& r) {
			if (l.value == r.value) return l.name < r.name;
			return l.value > r.value;
		});
		for (int i = 0; i < indx; i++) cout << ps[i].name << '\n';
	}
}

反思: 踩坑了,开始想用小数来表示value,一直wa,后来改成int才过


A - 咕咕东的目录管理器

目录管理器可以理解为要维护一棵有根树结构,每个目录的儿子必须保持字典序。
现在咕咕东可以在命令行下执行以下表格中描述的命令:

在这里插入图片描述
输入
输入文件包含多组测试数据,第一行输入一个整数表示测试数据的组数 T (T <= 20);

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

每组测试数据的 2 ~ Q+1 行为具体的操作 (MKDIR、RM 操作总数不超过 5000);

面对数据范围你要思考的是他们代表的 “命令” 执行的最大可接受复杂度,只有这样你才能知道你需要设计的是怎样复杂度的系统。
输出
每组测试数据的输出结果间需要输出一行空行。注意大小写敏感。

时空限制
Time limit 6000 ms

Memory limit 1048576 kB

样例输入
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


思路: 一定是个树形结构,题目要求根据子目录名字取到,那么是个map,而且马匹又能自动排序,一举两得。
树形结构采用数组形式,每个节点记录他的父节点,子目录存在map中,first是名字,second是index
特别注意有个撤销操作,需要在进行其他操作时把操作过程留在栈里
MKDIR:map加个孩子newNode,加到node数组中,栈中加入pair(“MKDIR”,newNode),撤销时把newNode从node[now]中删除即可
RM:删除仅仅是把孩子从map中删了eraseNode,node中留着方便撤销。栈中加入(“RM”,eraseNode)
CD:直接改变now即可,需要判断一下是否有这个目录或者如果是…是否有夫目录。栈中加入(“CD”,now)
SZ:要统计size就要遍历,极端情况就是整个树,算一算1e5*5000显然太大了,解决方案是每个节点记录其size,那么MKDIR和RM要修改这个值,注意整个树都要修改
LS:就列出来就行
TREE:如果采用遍历手段,极端情况要遍历所有节点,太耗时,采用懒更新的方案,没个节点记录pre和bck。求pre和bck的方案是递归,后来溢栈了,又改成了迭代
UNDO:从栈中取出来,pair的second是需要的信息,MKDIR的撤销的second记录的是创建的目录的index,把这个删了即可。其余操作类似


#include <iostream>
#include <stack>
#include <map>
#include <vector>
#include <cstring>
using namespace std;
constexpr int MAXN = 5000 + 10;
stack<pair<string, int>> s;
struct directory
{
	string name;
	map<string, int> mp;
	int fa = -1, sz = 1;
	vector<int> pre, bck;
	bool tag = false;
};
directory node[MAXN];
int indx, now;
bool vis[5000];
void init()
{
	indx = now = 0;
	while (s.size()) s.pop();
	directory root;
	root.name = "root";
	node[indx++] = root;
}
void update(int id, int num)
{
	while (id != -1)
	{
		node[id].tag = false;
		node[id].sz += num;
		id = node[id].fa;
	}
}
void mkdir()
{
	string str; cin >> str;
	directory& curNode = node[now];
	if (curNode.mp.count(str))
	{
		cout << "ERR'\n";
		return;
	}
	cout << "OK\n";
	directory newNode;
	newNode.name = str;
	newNode.fa = now;
	node[indx++] = newNode;
	curNode.mp[str] = indx - 1;//indx维持为尾后
	s.push(make_pair("MKDIRS", indx - 1));
	update(now, 1);
}

void rm()
{
	string str; cin >> str;
	directory& curNode = node[now];
	if (!curNode.mp.count(str))
	{
		cout << "ERR\n";
		return;
	}
	int eraseNode = curNode.mp[str];
	cout << "OK\n";
	curNode.mp.erase(str);
	update(now, -node[eraseNode].sz);
	s.push(make_pair("RM", eraseNode));
}

void cd()
{
	string str; cin >> str;
	directory& curNode = node[now];
	if (!(curNode.mp.count(str) || ((str == "..") && node[now].fa != -1)))
	{
		cout << "ERR\n";
		return;
	}
	cout << "OK\n";
	int theNode;
	if (str == "..") theNode = curNode.fa;
	else theNode = curNode.mp[str];
	s.push(make_pair("CD", theNode));
	now = theNode;
	return;
}

void sz()
{
	cout << node[now].sz << '\n';
	return;
}

void ls()
{
	int t = node[now].mp.size();
	if (t == 0)
	{
		cout << "EMPTY\n";
		return;
	}
	directory& curNode = node[now];
	auto pos = curNode.mp.begin();
	if (t >= 1 && t <= 10)
	{
		while (pos != curNode.mp.end())
			cout << (pos++)->first << '\n';
		return;
	}
	for (int i = 1; i <= 5; i++)
		cout << (pos++)->first << '\n';
	cout << "...\n";
	pos = curNode.mp.end();
	for (int i = 0; i < 5; i++) pos--;
	for (int i = 0; i < 5; i++)
		cout << (pos++)->first << '\n';
}

//弃用,因为溢栈了
//void _preTrack(int id, int cur)
//{
//	if (node[id].pre.size() >= 10) return;
//	node[id].pre.push_back(cur);
//	for (auto c : node[cur].mp)
//		_preTrack(id, c.second);
//}
void preTrack(int id)
{
	auto& curNode = node[id];
	stack<int> s;
	s.push(id);
	while (!s.empty()) {
		int tmp = s.top();
		s.pop();
		curNode.pre.push_back(tmp);
		if (curNode.pre.size() == 10) return;
		for (auto i = node[tmp].mp.rbegin(); i != node[tmp].mp.rend();i++)
			s.push((*i).second);
	}
}

//void _bckTrack(int id, int cur)
//{
//	if (node[id].bck.size() >= 5) return;
//	for (auto i = node[cur].mp.rbegin(); i != node[cur].mp.rend(); i++)
//		_bckTrack(id, (*i).second);
//	node[id].bck.push_back(cur);
//}
void bckTrack(int id)
{
	memset(vis, 0, sizeof(bool) * 5000);
	auto& curNode = node[id];
	stack<int> s;
	vector<int> v;
	s.push(id);
	while (v.size() < 5)
	{
		int tmp = s.top();
		while (node[tmp].mp.size() && !vis[tmp])
		{
			vis[tmp] = true;
			for (auto i = node[tmp].mp.begin(); i != node[tmp].mp.end(); i++)
			{
				s.push((*i).second);
			}
			tmp = (*(node[tmp].mp.rbegin())).second;
		}
		v.push_back(s.top()); s.pop();
	}
	for (auto i = v.rbegin(); i != v.rend(); i++)
		curNode.bck.push_back(*i);
}

void pushdown(int id)
{
	directory& theNode = node[id];
	theNode.pre.clear();
	theNode.bck.clear();
	preTrack(id);
	if (theNode.sz > 10) bckTrack(id);
	else theNode.bck = theNode.pre;
	theNode.tag = 1;
}
void tree()
{
	directory& curNode = node[now];
	if (!curNode.tag) pushdown(now);
	if (curNode.sz == 1) cout << "EMPTY\n";
	else if (curNode.sz > 1 && curNode.sz <= 10)
		for (auto c : curNode.pre) cout << node[c].name << '\n';
	else
	{
		for (int i = 0; i < 5; i++) cout << node[curNode.pre[i]].name << '\n';
		cout << "..\n";
		for (auto i = curNode.bck.begin(); i != curNode.bck.end(); i++)
			cout << node[*i].name << '\n';
	}
}

void undo()
{
	if (s.empty())
	{
		cout << "ERR\n";
		return;
	}
	cout << "OK\n";
	auto e = s.top(); s.pop();
	if (e.first == "MKDIR")
	{
		node[now].mp.erase(node[e.second].name);
		update(now, -1);
	}
	else if (e.first == "RM")
	{
		node[now].mp[node[e.second].name] = e.second;
		update(now, node[e.second].sz);
	}
	else
	{
		now = e.second;
	}
}
int main()
{
	int T; cin >> T;
	while (T--)
	{
		init();
		int Q; cin >> Q;
		while (Q--)
		{
			string op; cin >> op;
			if (op == "MKDIR") mkdir();
			else if (op == "RM") rm();
			else if (op == "CD") cd();
			else if (op == "SZ") sz();
			else if (op == "LS") ls();
			else if (op == "TREE") tree();
			else if (op == "UNDO") undo();
		}
	}
}

反思: 这个题的精髓在于size和tree操作,都要记录下来,以空间换时间,否则会超时,这类题要先想好时间复杂度,csp的题空间一般给的比较足,必要时可以用来换时间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值