C++ 排名选择投票
我目前正在编写一个程序,它根据一个包含3次测试选举的输入文本文件执行一系列的排名选择选举,每个测试选举有3名候选人中的5张选票。然后输出每次选举的获胜候选人 问题是,对于第一次测试选举,获胜者的输出显示为候选人-1获胜。假设是,候选人2获胜。基于第一次测试选举的结果 我尝试在int main之前将返回值从“-1”更改为“2”。它确实输出了我想要的东西,但我正在努力避免硬编码。如果有人能给我提示如何以其他方式解决这个问题,我将不胜感激 Text Fileelections.txt:C++ 排名选择投票,c++,C++,我目前正在编写一个程序,它根据一个包含3次测试选举的输入文本文件执行一系列的排名选择选举,每个测试选举有3名候选人中的5张选票。然后输出每次选举的获胜候选人 问题是,对于第一次测试选举,获胜者的输出显示为候选人-1获胜。假设是,候选人2获胜。基于第一次测试选举的结果 我尝试在int main之前将返回值从“-1”更改为“2”。它确实输出了我想要的东西,但我正在努力避免硬编码。如果有人能给我提示如何以其他方式解决这个问题,我将不胜感激 Text Fileelections.txt: 15 1 2
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 #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.
********************
在“当选候选人”的结尾,你有一个else语句,它做了一大堆无用的工作,因为你做了之后从来没有使用过它。你应该在另一个身体里决定一个胜利者,然后把它还给他。由于您没有这样做,函数末尾的return-1将被执行。这看起来像是一个学习练习,您需要自己解决,我有一个不为这些编写代码的策略 然而,如果我能提出一个建议的话:试着把每一张选票代表为一组候选人,按偏好的递增顺序存储,也就是说,最后一个选择,下一个选择,…,第二个选择,第一个选择。然后将选票存储在一个数据库中,该数据库的键是当前每张选票所选的候选人。编辑:std::无序的_多重映射更好。您可以插入拥有选票数据的对象,或者只是移动向量,并为自己节省额外的间接层次。例如:
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 >;
一些测试用例。末尾的换行不是可选的
# Test data for rankedchoice.cpp
# Expected output: 4
8
1 4 3 2
2 4 3 1
2 1 4 3
3 1 2 4
3 2 1 4
3 1 4 2
4 2 3 1
4 1 3 4
# Expected output: 4
8
4 1 3 4
4 2 3 1
3 2 1 4
3 1 2 4
2 4 3 1
2 1 4 3
1 4 3 2
3 1 4 2
# Expected output: 1
1
1 2
# Expected output: 1
1
1
# Expected output: 2
3
1 2
2 1
2
# Expected output: 1 2
2
1 2
2 1
# Expected output: 1 3
6
1 2
1 3
1
3 2
3 1
2 3
# Expected output: 1
# Because there are no first-place votes for 4, it should be eliminated,
# and the 3 4 1 2 ballots should put 1 over the top on the second ballot.
9
1 2 3 4
1 2 3 4
1 2 3 4
2 1 3 4
2 4 1 3
2 4 1 4
2 4 1 3
3 4 1 2
3 4 1 2
# Expected Output: 3
5
1 2 3
2 1 3
3 1 2
3 2 1
3
# Expected Output: 3
5
1 2 3
1 3 2
2 3 1
3 1 2
3 2 1
你试过使用调试器了吗?顺便说一句,你应该做更多的分而治之。识别代码中可以命名的部分,并使它们成为某个类的函数或方法。如果这样做,在每次调用其中一个部分后,您可以通过调试器或打印查看当前变量并进行验证,找出哪个部分工作错误。
// 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;
}
# Test data for rankedchoice.cpp
# Expected output: 4
8
1 4 3 2
2 4 3 1
2 1 4 3
3 1 2 4
3 2 1 4
3 1 4 2
4 2 3 1
4 1 3 4
# Expected output: 4
8
4 1 3 4
4 2 3 1
3 2 1 4
3 1 2 4
2 4 3 1
2 1 4 3
1 4 3 2
3 1 4 2
# Expected output: 1
1
1 2
# Expected output: 1
1
1
# Expected output: 2
3
1 2
2 1
2
# Expected output: 1 2
2
1 2
2 1
# Expected output: 1 3
6
1 2
1 3
1
3 2
3 1
2 3
# Expected output: 1
# Because there are no first-place votes for 4, it should be eliminated,
# and the 3 4 1 2 ballots should put 1 over the top on the second ballot.
9
1 2 3 4
1 2 3 4
1 2 3 4
2 1 3 4
2 4 1 3
2 4 1 4
2 4 1 3
3 4 1 2
3 4 1 2
# Expected Output: 3
5
1 2 3
2 1 3
3 1 2
3 2 1
3
# Expected Output: 3
5
1 2 3
1 3 2
2 3 1
3 1 2
3 2 1