目录
题目
在遥远的相簿国有两个种族,分别是冬马族和雪菜族,每个人都属于这两个种族之一。如今,又到了选举元首的时候了,所有 n 个人排成一排等待投票。
相簿国的选举制度比较奇特,并不是看谁的票数多,而是使用一票否决制度,并且所有人都可以投一张票或者不投票。如果排在前面的人把排在后面的人一票否决了,那么后面的人不能参与竞选,也不能投出他的那一票了。而如果排在后面的人把排在前面的人一票否决了,那么排在前面的人不能参与竞选,但是已经投出的那一票仍然有效。如果一轮投完仍然有不只一个竞选者,那么这些留下的人再投一轮,还没有停止就再投一轮……直到剩下一人为止,但投票的先后顺序不变。
为了保证每轮投票中都有更多人可以投票,规定竞选者优先给在他投票时已经投过票的异族人,每轮投票结束时重置竞选人的投票状态。
所有人都希望自己族的人能当选元首(而不是希望自己当元首),并且会为此采取最优策略。现在给出排队中每个人的种族,请问最后是哪个种族的人会当选元首?
输入
输入第一行一个数 n ,表示相簿国里的人数。
第二行一个长度为 n 的字符串 s ,从前到后依次表示排队投票的人所属的种族。D 表示冬马族,而 X 表示雪菜族。
输出
输出一个字符 D 或 X ,表示是哪一族的人当选。
注意
数据保证1<=n <=200000。
测试用例
用例1.
输入:
5↵
DDXXX↵
输出:
D↵
用例2.
输入:
6↵
DDXXXX↵
输出:
X↵
思路
我们首先需要明确每一个族人有两种权利:投票权和竞选权。
因此,
我们可以用D和X分别记录有投票权的族人;
使用栈(stack)来存放仍有竞选权的族人。
值得注意的是,
前面的一个人能够让后面的异族人同时丧失投票权和竞选权,
因为“如果排在前面的人把排在后面的人一票否决了,那么后面的人不能参与竞选,也不能投出他的那一票了”;
而要让前面的一个人丧失投票权和竞选权则需要后面的两个异族人,
因为“如果排在后面的人把排在前面的人一票否决了,那么排在前面的人不能参与竞选,但是已经投出的那一票仍然有效”
简单来说,是因为需要一个异族人去花掉前面的人的投票权,另一个异族人使用竞选权让前面的人丧失竞选权。
具体步骤
代码首先输入选票的数量和选票字符串。然后遍历字符串,检查当前选票是“D”还是“X”。根据当前选票和栈中现有的竞选人,确定具有当前选票的候选人是否有权竞选或需要被淘汰。
如果当前选票是“D”且没有具有投票权的“X”候选人,则将“D”候选人加入栈中,并增加“D”票数。如果栈中有“X”候选人,则“D”候选人可以淘汰栈中的所有“X”候选人,并将自己加入栈中。
如果当前选票是“X”且没有具有投票权的“D”候选人,则将“X”候选人加入栈中,并增加“X”票数。如果栈中有“D”候选人,则“X”候选人可以淘汰栈中的所有“D”候选人,并将自己加入栈中。
在遍历完所有选票后,代码计算栈中剩余的候选人数量。栈中候选人数量较多的种族被宣布为获胜者。如果剩余候选人数量对于两个种族来说是相同的,则根据栈中最后一个候选人确定获胜者。
C++完整代码(含详细注释)
#include <iostream>
#include <stack>
using namespace std;
int main() {
int n;
string s;
stack<char> st;//栈用于存放还有竞选权的人
int D = 0, X = 0;//记录两个种族的投票权
cin >> n;
cin >> s;
for (int i = 0; i < s.size(); i++) {
if (s[i] == 'D') {//遍历到D时
if (X == 0) {//如果X的投票权为0,则说明D有竞选权和投票权
if (st.empty()) {//栈为空
st.push('D');
D++;
}
else {//栈不为空
if (st.top() == 'D') {//栈顶为D
st.push('D');
D++;
}
else {//栈顶为X,则需要D使用投票权将前面的X淘汰,但自己仍有竞选权
st.pop();//将前面的X淘汰
int num = 1;//记录X的淘汰人数和D的竞选人数
while (i<n-1&&s[i + 1] == 'D' &&!st.empty() && st.top() == 'X') {
//在字符串下个仍为D且栈顶仍为X时的,可以连续将X一次性连续淘汰
i++;
st.pop();
num++;
}
while (num--) {//将有竞选权的D入栈
st.push('D');
}
}
}
}
else {//如果X的投票权不为0,则X使用投票权让D淘汰
X--;
}
}
else if (s[i] == 'X') {//遍历到X时
if (D == 0) {//如果D的投票权为0,则说明D有竞选权和投票权
if (st.empty()) {//栈为空
st.push('X');
X++;
}
else {//栈不为空
if (st.top() == 'X') {//栈顶为X
st.push('X');
X++;
}
else {//栈顶为D,则需要X使用投票权将前面的X淘汰,但自己仍有竞选权
st.pop();
int num = 1;//记录D的淘汰人数和X的竞选人数
while (i < s.size() - 1 && s[i + 1] == 'X' && !st.empty() && st.top() == 'D') {
// //在字符串下个仍为X且栈顶仍为D时的,可以连续将D一次性连续淘汰
i++;
st.pop();
num++;
}
while (num--) {//将有竞选权的X入栈
st.push('X');
}
}
}
}
else {//如果D的投票投票权不为0
D--;
}
}
}
int D1 = 0, X1 = 0;//用于记录竞选者
char last;
while (!st.empty()) {//计算剩余竞选者
if (st.size() == 1) last = st.top();
if (st.top() == 'D') D1++;
else X1++;
st.pop();
}
//哪个种族剩余的竞选者多,哪个种族就会赢得竞选
if (D1 > X1) cout << 'D' << endl;
else if (X1 > D1) cout << 'X' << endl;
else cout << last << endl;//竞选者人数相同,则在前面(即栈顶)的种族会赢,因为前面的可以让后面的失去投票权
return 0;
}