我目前正在编写一个程序,该程序将基于包含3个测试选举的输入文本文件执行一系列排名选择的选举,每个候选选举中有3个候选人中的5个投票。然后输出每次选举的获胜候选人。
问题在于,对于第一次测试选举,获胜者的输出显示为“候选人#-1获胜”。假设是“候选人#2获胜”。根据第一次测试选举的结果。
我尝试在int main()之前将返回值从“ -1”更改为“ 2”。它确实输出了我想要的东西,但是我试图避免硬编码。如果有人可以通过其他方法给我提示如何解决此问题,我将不胜感激!
文本文件(elections.txt):
15
1
2
3
3
2
1
2
1
3
1
2
3
2
3
1
15
1
2
3
1
2
3
1
2
3
1
2
3
2
1
3
15
3
2
1
3
2
1
3
1
2
1
2
3
1
3
2
我的代码:
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <iomanip>
using namespace std;
const char *FILENAME = "elections.txt";
const int MAXBALLOTS = 500;
const int NUM_CANDIDATES = 3;
int elect_candidate(int ballots[MAXBALLOTS][NUM_CANDIDATES],
int numBallots) {
int tally[NUM_CANDIDATES + 1] = { 0 };
double percentages[NUM_CANDIDATES + 1];
for (int i = 0; i < numBallots; i++) {
int j;
for (j = 0; j < NUM_CANDIDATES; ++j) {
if (ballots[i][j] > 0)
break;
}
if (j < NUM_CANDIDATES) {
int choice = ballots[i][j];
tally[choice]++;
}
}
int best = 1;
int bestCount = 0;
int worst = 1;
cout << "Percentages for each candidate: " << endl;
for (int i = 1; i < NUM_CANDIDATES + 1; i++) {
percentages[i] = (double)tally[i] / numBallots;
cout << fixed << setprecision(2);
cout << "#" << i << ": " << percentages[i];
cout << endl;
if (tally[i] > tally[best]) {
best = i;
bestCount = 1;
} else if (tally[i] == tally[best]) {
++bestCount;
} else if (tally[i] < tally[worst]) {
worst = i;
}
}
if (best == worst) {
return 0;
}
if (2 * tally[best] > numBallots) {
return best;
} else {
for (int i = 0; i < numBallots; i++) {
for (int j = 0; j < NUM_CANDIDATES; ++j) {
if (tally[ballots[i][j]] == tally[worst]) {
ballots[i][j] = 0;
}
}
}
}
return -1;
}
int main()
{
ifstream f(FILENAME);
string tmp;
int nLines;
int numBallots = 0;
int ballots[MAXBALLOTS][NUM_CANDIDATES];
cout << "********************" << endl;
cout << "C++ Election of 2020" << endl;
cout << "********************" << endl;
// While we are not at end-of-file
while (getline(f, tmp)) {
// Read the number of lines for this election
nLines = atoi(tmp.c_str());
// Read in each ballot
for (int i = 0; i < nLines; i += 3) {
// Read in a single ballot (3 lines each)
cout << "Read ballot: ";
for (int j = 0; j < NUM_CANDIDATES; j++) {
getline(f, tmp);
ballots[numBallots][j] = atoi(tmp.c_str());
cout << " " << ballots[numBallots][j];
}
numBallots++;
cout << endl;
}
cout << "Read " << numBallots << " ballots..." << endl;
cout << endl;
int winner = -1;
// Run the election
winner = elect_candidate(ballots, numBallots);
cout << "********************" << endl;
cout << "Candidate #" << winner << " wins." << endl;
cout << "********************" << endl;
cout << endl;
numBallots = 0;
}
return 0;
}
我的输出:
********************
C++ Election of 2020
********************
Read ballot: 1 2 3
Read ballot: 3 2 1
Read ballot: 2 1 3
Read ballot: 1 2 3
Read ballot: 2 3 1
Read 5 ballots...
Percentages for each candidate:
#1: 0.40
#2: 0.40
#3: 0.20
********************
Candidate #-1 wins.
********************
Read ballot: 1 2 3
Read ballot: 1 2 3
Read ballot: 1 2 3
Read ballot: 1 2 3
Read ballot: 2 1 3
Read 5 ballots...
Percentages for each candidate:
#1: 0.80
#2: 0.20
#3: 0.00
********************
Candidate #1 wins.
********************
Read ballot: 3 2 1
Read ballot: 3 2 1
Read ballot: 3 1 2
Read ballot: 1 2 3
Read ballot: 1 3 2
Read 5 ballots...
Percentages for each candidate:
#1: 0.40
#2: 0.00
#3: 0.60
********************
Candidate #3 wins.
********************
预期输出:
********************
C++ Election of 2020
********************
Read ballot: 1 2 3
Read ballot: 3 2 1
Read ballot: 2 1 3
Read ballot: 1 2 3
Read ballot: 2 3 1
Read 5 ballots...
Percentages for each candidate:
#1: 0.40
#2: 0.40
#3: 0.20
********************
Candidate #2 wins.
********************
Read ballot: 1 2 3
Read ballot: 1 2 3
Read ballot: 1 2 3
Read ballot: 1 2 3
Read ballot: 2 1 3
Read 5 ballots...
Percentages for each candidate:
#1: 0.80
#2: 0.20
#3: 0.00
********************
Candidate #1 wins.
********************
Read ballot: 3 2 1
Read ballot: 3 2 1
Read ballot: 3 1 2
Read ballot: 1 2 3
Read ballot: 1 3 2
Read 5 ballots...
Percentages for each candidate:
#1: 0.40
#2: 0.00
#3: 0.60
********************
Candidate #3 wins.
********************
答案 0 :(得分:1)
这看起来像是您想自己解决的学习练习,并且我有一项政策,不要为这些人编写代码。
但是,如果我可以提出建议:请尝试将每个选票表示为std::vector
个候选人,并按优先级递增的顺序存储,即最后选择,倒数第二个,...,第二个选择,第一选择。然后将选票存储在std::multimap
中,其关键字是每个选票当前已选择的候选者。 (编辑:一个std::unordered_multimap
更好。)您可以插入拥有选票数据的smart pointers,或者只是移动向量并为自己节省额外的间接层。例如:
using Candidate = int;
// A ranking of candidates in order of last-place to first-place:
using Ballot = std::vector<Candidate>;
// A collection of ballots, each keyed to the highest-ranking candidates
// on the ballot who is still alive:
using BallotBox = std::unordered_multimap< Candidate, Ballot >;
因此,如果我更喜欢爱丽丝(Alice)和鲍勃(Bob)而不是卡罗尔(Carol),那么我的选票将存储在BallotBox
的{{1}}内,其密钥为BallotNode
Candidate
,这是我的首选,并且引用了选票数据alice
。如果消除了爱丽丝(Alice),该算法将缩短{carol, bob, alice}
向量的长度,使其现在变为Ballot
,并将{carol, bob}
键更新为BallotNode
,这是我的新选择。 / p>
您可以使用ballot_box.emplace(BallotNode( candidate, ballot ))
将每个选票插入bob
(多地图容器)中。然后,您可以将每个选票插入到输入循环中的BallotBox
中,并将BallotBox
本身的所有权传递给要使用的计票功能。
计算候选人的票数是成员函数ballot_box.count(that_jerk)
。代表给定候选人的选票的节点是ballot_box.equal_range(loser)
范围内的节点。
每个BallotBox
是一个向量,其最优选的选项随后存储。因此,找到其下一个选择就是简单地减小向量的长度。这是微不足道的恒定时间操作。如果您反复重申直到找到仍在比赛中的候选人,您便会保持不变,即任何选票的最后一个要素都是其当前最优先的选择。
然后,您可以通过iterating over them,extracting每个与密钥匹配的Ballot
来重新分配每个淘汰候选人的所有选票,找出其中包含的BallotNode
上的邪恶程度较小,制作一个新的节点,该节点的键是选票上的下一个候选对象,然后将新节点插入或嵌入到多图中。
您必须创建一个要插入的新节点,并且不能安全地重复使用提取的节点,因为您需要更新其密钥,该密钥是恒定的。但是,您可以移动而不是复制其选票数据。
您不解释排名选择算法的详细信息,尤其是是否有决胜局。例如,如果第一个决胜局是第一名的票数,第二个决胜局是第二名的票数,依此类推,在开始修改数据之前,您需要复制决胜分数据。在您的投票箱中。
最后,由于可以建立平局,请考虑定义一种可以返回一个或多个获胜者的结果类型,例如Ballot
。
我等了几天,我改变了输入格式,以至于你不能仅仅把它打开。但这是一个很有趣的问题,我自己解决了一个问题,并且学到了一些东西。我遇到的最棘手的错误是,从投票箱中提取节点会使迭代器无效,因此我不得不更改如何为失败的候选人投票。
然后,我回过头来,将数据结构std::set<Candidate>
和std::multimap
(旨在使用树)更改为std::set
和std::unordered_multimap
(旨在使用哈希)表格)。这样不仅可以加快访问速度,而且如果您事先知道输入大小,则可以使用std::unordered_set
预先进行分配。
最终结果没有任何手动的内存管理,进行任何深拷贝,编写任何新类甚至创建任何智能指针。它只是移动STL容器。
.reserve()
一些测试用例。 (末尾的换行符不是可选的。)
// This program requires C++17 or higher.
/* For the purposes of this exercise, data is read from standard input.
* Data consists of zero or more election tallies, terminated by newlines.
* The input encoding is UTF-8. Lines beginning with # are comments, and
* ignored. Parsing and input-checking are minimal.
*
* Each election tally consists of:
* - One line consisting of the number of ballots, N
* - N ballots, each on its own line, consisting of space-separated integers,
* each identifying a candidate. Higher-ranked candidates appear before
* lower-ranked ones on each ballot, no candidate may appear more than
* once on the same ballot, and a candidate must be the first choice of at
* least one voter to win.
*
* The expected output is, for each election tally:
* The ID of the inning candidates (or a space-separated list of all candid=
* ates tied for first place) followed by a newline.
*
* If more than one candidate is tied for last place, which last-place can-
* didate is eliminated is arbitrary. This could lead to an ambifuous result.
* The algorithm doesn’t do tiebreakers (such as most first-place votes).
*/
#include <algorithm>
#include <array>
#include <assert.h>
#include <iostream>
#include <limits>
#include <memory>
#include <stdexcept>
#include <stdlib.h>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
using std::cerr;
using std::cin;
using std::cout;
using std::endl;
using Candidate = int;
// A ranking of candidates in order of last-place to first-place:
using Ballot = std::vector<Candidate>;
// A collection of ballots, each keyed to the highest-ranking candidates
// on the ballot who is still alive:
using BallotBox = std::unordered_multimap< Candidate, Ballot >;
using CandidateSet = std::unordered_set<Candidate>;
// Magic constant to make turn off lenght-checking:
constexpr auto huge_size = std::numeric_limits<std::streamsize>::max();
template <class It>
std::ostream& print_list( std::ostream& out,
const It& begin,
const It& end )
/* Prints the elements in range to the provided stream, separated by spaces.
* The type It must be a forward iterator. Utility function intended to be
* called by operator<< overloads.
*/
{
if (begin != end) {
out << *begin;
It it = begin;
++it;
while ( it != end )
out << ' ' << *it++;
}
return out;
}
inline std::ostream& operator<<( std::ostream& out, const CandidateSet& x )
{
return print_list( out, x.cbegin(), x.cend() );
}
inline std::ostream& operator<<( std::ostream& out, const Ballot& x )
{
return print_list( out, x.cbegin(), x.cend() );
}
CandidateSet get_unique_keys( const BallotBox& x ) noexcept
/* Generates the set of keys in x.
*/
{
CandidateSet results;
if (!x.empty()) {
auto it = x.cbegin();
const Candidate* previous = &it->first;
results.emplace(*previous);
++it;
while (it != x.cend()) {
if (it->first != *previous) {
previous = &it->first;
results.emplace(*previous);
}
++it;
} // end while
} // end if
return results; // Guaranteed copy elision.
}
BallotBox collect_ballots( std::istream& in = cin )
/* Creates the first round of the next election in the input stream, or
* else throws a std::runtime_error.
*/
{
unsigned n_votes;
in >> n_votes;
if (!in)
throw std::runtime_error("Expected: number of votes.");
if ( in.peek() == '\n' )
in.get();
else
throw std::runtime_error("Expected: newline.");
BallotBox ballot_box;
ballot_box.reserve(n_votes);
while (n_votes--) {
while( in.peek() == '#' )
in.ignore( huge_size, '\n');
Ballot ballot;
do {
Candidate c;
in >> c;
if (!in)
throw std::runtime_error("Expected: Candidate ID.");
ballot.push_back(c);
} while ( in.get() == ' ' );
// The above never checks which non-space character it consumed, but it
// should have been a newline.
// For convenience, we inserted elements in the reverse order that our
// algorithm needs. Reversing is faster than front-insertions.
std::reverse( ballot.begin(), ballot.end() );
// Now we need to insert a node keyed to the first choice into the
// BallotBox (multimap).
const Candidate kodos = ballot.back();
ballot_box.emplace( kodos, std::move(ballot) );
}
while (in && !in.eof() && in.peek() == '\n')
in.get(); // Chomp trailing newlines.
return ballot_box; // Guaranteed copy elision.
}
CandidateSet count_ballots( BallotBox&& ballot_box )
/* Consumes the initial state of the election and returns the Results of the
* election.
*/
{
using Tally = BallotBox::size_type;
constexpr Tally votes_for_Stalin =
std::numeric_limits<Tally>::max();
constexpr Candidate noman = -1;
CandidateSet candidates = get_unique_keys(ballot_box);
Tally most_votes = 0;
Tally fewest_votes = votes_for_Stalin;
Candidate loser = noman;
Candidate winner = noman;
for ( const Candidate i : candidates ) {
const Tally votes = ballot_box.count(i);
if (votes > most_votes) {
most_votes = votes;
winner = i;
}
if (votes < fewest_votes) {
fewest_votes = votes;
loser = i;
}
} // end for
while ( most_votes < (ballot_box.size()/2U + 1U) &&
most_votes > fewest_votes &&
!candidates.empty() &&
!ballot_box.empty() ) {
std::vector<Ballot> changed_votes;
changed_votes.reserve(fewest_votes);
candidates.erase(loser);
while ( auto handle = ballot_box.extract(loser) ) {
Ballot& ballot = handle.mapped();
do {
ballot.pop_back();
} while ( candidates.find(ballot.back()) == candidates.end() );
if (!ballot.empty()) {
changed_votes.emplace_back(std::move(ballot));
}
} // end while
for ( Ballot& b : changed_votes ) {
assert(!b.empty());
const Candidate new_key = b.back();
ballot_box.emplace( std::move(new_key), std::move(b) );
}
most_votes = 0;
fewest_votes = votes_for_Stalin;
loser = noman;
winner = noman;
for ( const Candidate i : candidates ) {
const auto votes = ballot_box.count(i);
if (votes > most_votes) {
most_votes = votes;
winner = i;
}
if (votes < fewest_votes) {
fewest_votes = votes;
loser = i;
} // end if
} // end for
} // end while
if ( most_votes > fewest_votes ) {
/* If this branch is reached, the while loop did not fail because all the
* remaining candidates were tied: one candidate got more votes than
* another. Nor did it terminate because either the set of remaining can-
* didates or the container of votes were empty. Therefore, the loop
* terminated for the only other possible reason: one candidate has won
* a majority.
*/
candidates.clear();
candidates.insert(winner);
}
return candidates; // Guaranteed copy elision.
}
int main()
{
try {
while( cin && !cin.eof() ) {
const auto next = cin.peek();
if ( next == '#' || next == '\n' )
cin.ignore( huge_size, '\n');
else {
cout << count_ballots(collect_ballots()) << endl;
} // end if
} // end while
if (cin.fail())
throw std::runtime_error("Failed to read from standard input.");
} catch (const std::runtime_error& e) {
cout.flush();
cerr << "Error: " << e.what() << '\n';
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
答案 1 :(得分:0)
在elect_candidate
的末尾,您有一条else
语句,它执行了许多无用的工作,因为您在执行此操作后再也不会使用它。您应该确定该else
内的获胜者并将其退回。由于您未执行此操作,因此将执行函数末尾的return -1
。