编辑我确实注意到了这个程序中的一个小错误: - 当我最初提交它时,它只针对单个测试用例完成;然后我回到我的 IDE 以改进算法以适用于多个测试用例。在我开始工作后,我回到这里编辑这篇文章,但我错过了几行关键的行。它们现在已修复,如果想要保留每个测试用例的相关记录及其相应的惩罚值,我还添加了一些其他可以添加到此类的 cmets。 结束编辑
我将它封装到一个类中。我很确定可能有一种更优雅的方式来做到这一点,但这就是我想出的,使用你的文本文件示例来填充数据结构和返回惩罚的方法。
pieces.txt
1
15 8 3
3 8
11 2
8 6
DefenderBoard.h
#ifndef DEFENDER_BOARD_H
#define DEFENDER_BOARD_H
#include <fstream>
#include <vector>
#include <algorithm>
struct Coord {
unsigned x;
unsigned y;
Coord() : x(0), y(0) {}
Coord( unsigned xIn, unsigned yIn ) : x( xIn ), y( yIn ) {}
};
class DefenderBoard {
private:
std::string filename;
unsigned testcase;
unsigned gridsize_x;
unsigned gridsize_y;
unsigned numTowers;
std::vector<unsigned> penalties;
std::vector<Coord> cells;
public:
explicit DefenderBoard( const std::string& filenameIn );
~DefenderBoard();
void getPenalties( std::vector<unsigned>& penalties ) const;
private:
void getDataFromFile();
void calculatePenalty();
};
#endif // DEFENDER_BOARD_H
DefenderBoard.cpp
#include "DefenderBoard.h"
DefenderBoard::DefenderBoard( const std::string& filenameIn ) :
filename( filenameIn ),
gridsize_x( 0 ), gridsize_y( 0 ),
testcase( 0 ),
numTowers( 0 ),
penalty( 0 ) {
getDataFromFile();
}
DefenderBoard::~DefenderBoard() {
}
void DefenderBoard::getPenalties( std::vector<unsigned>& penaltiesOut ) const {
penaltiesOut = penalties;
}
void DefenderBoard::getDataFromFile() {
std::ifstream file;
file.open( filename );
if ( !file.is_open() ) {
// Note: This ExceptionHandler will not work for you.
// You will need to supply your own error or exception handling.
std::ostringstream strStream;
strStream << __FUNCTION__ << " failed to read in file[" << filename << "]";
throw ExceptionHandler( strStream );
}
file >> testcase;
// Temps
Coord towerCoord;
// t -> testcase | c - tower coords
for ( unsigned t = 0; t < testcase; ++t ) {
file >> gridsize_x;
file >> gridsize_y;
file >> numTowers;
for ( unsigned c = 0; c < numTowers; ++c ) {
file >> towerCoord.x;
file >> towerCoord.y;
cells.push_back( towerCoord );
}
calculatePenalty();
// After Penalty is calculated this test case along with the penalty value
// can be stored into a struct containing both the penalty and a vector of cells
// which would be done here and then that struct would be stored into another container to this class
// If the above is choosen to be done then this needs to be called here instead of within the calculate function
// Clear our cells so it can be reused for each test case found in this file.
cells.clear();
}
file.close();
}
bool compareCoordsX( const struct Coord& a, const struct Coord& b ) {
return a.x > b.x;
}
bool compareCoordsY( const struct Coord& a, const struct Coord& b ) {
return a.y > b.y;
}
void DefenderBoard::calculatePenalty() {
std::vector<unsigned> xValDiff;
std::vector<unsigned> yValDiff;
unsigned diff = 0;
// First Sort By Largest X - Then Find The Differences
std::stable_sort( cells.begin(), cells.end(), compareCoordsX );
unsigned idx = 0;
for ( ; idx < cells.size(); ++idx ) {
// For First Iteration Only
if ( idx == 0 ) {
diff = gridsize_x - cells[idx].x;
xValDiff.push_back( diff );
} else {
diff = cells[idx-1].x - cells[idx].x - 1; // Don't Forget to Subract 1
xValDiff.push_back( diff );
}
}
// Also push back the last value - 1
xValDiff.push_back( cells.back().x - 1 );
// Do Same Thing For Y
std::stable_sort( cells.begin(), cells.end(), compareCoordsY );
idx = 0;
diff = 0;
for ( ; idx < cells.size(); ++idx ) {
// First Iteration Only
if ( idx == 0 ) {
diff = gridsize_y - cells[idx].y;
yValDiff.push_back( diff );
} else {
diff = cells[idx-1].y - cells[idx].y - 1; // Don't Forget to Subtract 1
yValDiff.push_back( diff );
}
}
// Also push back the last value - 1
yValDiff.push_back( cells.back().y - 1 );
unsigned largestX = xValDiff[0];
unsigned largestY = yValDiff[0];
idx = 0;
for ( ; idx < cells.size(); ++idx ) {
if ( xValDiff[idx] > largestX ) {
largestX = xValDiff[idx];
}
if ( yValDiff[idx] > largestY ) {
largestY = yValDiff[idx];
}
}
// Calculate Penalty And Store It
// EDIT - I did update this code after I had originally posted it
// and when I added the update I did forget to change this commented section
// penalty = largestX * largestY;
// It should be like this:
penalties.push_back( largestX * largestY );
// I did have this line of code here too but I moved it out of this function and into the getDataFromFile() method.
// cells.clear();
}
main.cpp
#include <iostream>
#include "DefenderBoard.h"
int main() {
std::vector<unsigned> penalties;
DefenderBoard board( "pieces.txt" );
board.getPenalties( penalties );
unsigned idx = 0;
for ( ; idx < penalties.size(); ++idx ) {
std::cout << penalties[idx] << " ";
}
std::cout << std::endl;
return 0;
}
输出
12
第二次运行 - 两个测试用例:
pieces.txt
2
15 8 3
3 8
11 2
8 6
12 10 4
2 2
9 7
3 9
8 5
输出
12 8
注意:
没有边界检查来查看文件中的单元格坐标是否大于 MxN 板的大小。因此,如果网格大小是 8x8,并且有一块坐标为 [12,2] 或 [5,9],这些将破坏算法,给您无效的结果。我将把这些错误案例留作练习。
算法的实现
该算法的思想是先取大小并减去最远的部分。然后你将取它并从中减去下一个最远的部分并减去 1,直到你到达最后一块,然后最后你将取最后一块本身并从中减去 1。这将为您提供一个方向的所有差异。对另一个方向也重复此操作。然后您正在寻找 x 和 y 中的最大尺寸。一旦你有了它们,你需要做的就是计算 x 和 y 方向上最大差异的乘积,这将为你提供最大的开放区域。
我还注意到代码中存在某种类型的重复,也可以通过向类中添加几个较小的函数来重构它,但是为了显示算法,我认为在这种情况下并不需要它.该算法唯一的双重 for 循环是从文件中读取数据时。实际计算来自坐标对的线性向量。有 3 个遍历向量的单个 for 循环。前两个相同,一次在 x 方向,一次在 y 方向。所以时间取决于输入 N 的大小或长度。这应该是常数时间。最后一个 for 循环仍然是 N,但要查看大小为 N+1 的向量,该向量仍然是常数时间,其中 N 是塔或坐标对的数量。权衡的是,由于本地向量存储 x 和 y 值的差异,因此存在一点内存开销,对于小型和中等数据集,这不应该是一个问题。
免责声明
在对我的回答的评论中提到了这一点:
感谢您的努力,如果这有负面影响,我很抱歉……但我想知道人们何时会将这种类型的“封装”作为 OO 课程的一部分教授到“类”中……这可能是我的非常个人的品味,但是私有成员函数不带参数并返回 void 对我来说有点危险。在阅读代码时,必须跟踪所有可能被修改的成员,就像在编程的黑暗时代那样拥有全局变量一样糟糕。在这种特殊情况下,它们有助于隐藏第二个测试用例同时使用测试用例 1 和 2 中的塔的错误
是的,代码中存在错误,我已按照上述方法修复了该错误。现在,如果查看此类对象或 OO 方法的整体设计,他们会发现该实现对此类对象的用户或调用者是隐藏的。他在这里发生的事情有一个特定的顺序。
分步算法
- 使用用户定义的构造函数声明此类型的对象。
- 构造函数采用一个字符串作为文件名来读取数据。
- 文件打开,如果成功则开始读取数据,否则抛出异常。
- 它找出有多少个测试用例,并为每个测试用例执行以下操作:
- 检索塔的数量。
- 获取此测试用例中每个塔的坐标位置。
- 计算此测试用例的所有塔的罚分。
- 存储此案例的惩罚值。
- 对所有测试用例重复此过程。
- 关闭文件流。
- 对象构造完成。
- 现在对象已经完成,剩下的唯一事情就是让用户调用它的公共接口方法来检索惩罚值。
其他功能 - 这些可以添加 -
- 根据网格 MxN 大小检查塔坐标的边界。
- 所有案例的统计数据 - 重复次数、平均值、中位数、众数、标准偏差等。
- 内置打印方法。
- 附加容器,用于存储所有塔单元位置以及每个测试用例的相关惩罚值。
- 更多错误检查。
- 一些重构和效率改进。
修正 lisyarus 在 cmets 中提出了一个很好的观点,基于此,这里是非 OO 版本。
#include <string>
#include <vector>
#include <algorithm>
#include <fstream>
struct TowerCoordinate {
unsigned x;
unsigned y;
TowerCoordinate() : x(0), y(0) {}
TowerCoordinate( unsigned xIn, unsigned yIn ) : x( xIn ), y( yIn ) {}
};
struct GridSize {
unsigned width;
unsigned height;
GridSize() : width( 0 ), height( 0 ) {}
GridSize( unsigned widthIn, unsigned heightIn ) : width( widthIn ), height( heightIn ) {}
};
bool compareCoordsX( const struct TowerCoordinate& a, const struct TowerCoordinate& b ) {
return a.x > b.x;
}
bool compareCoordsY( const struct TowerCoordinate& a, const struct TowerCoordinate& b ) {
return a.y > b.y;
}
// Returns A Single Penalty
unsigned calculatePenalties( std::vector<TowerCoordinate>& towerLocations, GridSize& gridSize ) {
std::vector<unsigned> xValDiff, yValDiff;
unsigned diff = 0;
unsigned idx = 0;
// First Sort By Largest X - Then Find All Differences
std::stable_sort( towerLocations.begin(), towerLocations.end(), compareCoordsX );
for ( ; idx < towerLocations.size(); ++idx ) {
if ( idx == 0 ) {
diff = gridSize.width - towerLocations[idx].x;
xValDiff.push_back( diff );
} else {
diff = towerLocations[idx-1].x - towerLocations[idx].x - 1; // Don't Forget To Subtract 1
xValDiff.push_back( diff );
}
}
// Also Push Back (Last Value - 1)
xValDiff.push_back( towerLocations.back().x - 1 );
// Sort By Largest Y - Then Find All Differences
// First Sort By Largest X - Then Find All Differences
idx = 0;
diff = 0;
std::stable_sort( towerLocations.begin(), towerLocations.end(), compareCoordsY );
for ( ; idx < towerLocations.size(); ++idx ) {
if ( idx == 0 ) {
diff = gridSize.height - towerLocations[idx].y;
yValDiff.push_back( diff );
} else {
diff = towerLocations[idx-1].y - towerLocations[idx].y - 1; // Don't Forget To Subtract 1
yValDiff.push_back( diff );
}
}
// Also Push Back (Last Value - 1)
yValDiff.push_back( towerLocations.back().y - 1 );
unsigned largestX = xValDiff[0];
unsigned largestY = yValDiff[0];
idx = 0;
for ( ; idx < towerLocations.size(); ++idx ) {
if ( xValDiff[idx] > largestX ) {
largestX = xValDiff[idx];
}
if ( yValDiff[idx] > largestY ) {
largestY = yValDiff[idx];
}
}
return (largestX * largestY);
}
// Returns The Results Of All The Penalties For Each Case
std::vector<unsigned> getDefenderDataFromFile( const std::string& filename, unsigned& numTestCases, GridSize& gridSize, unsigned& numTowers, std::vector<TowerCoordinate>& towerLocations ) {
std::ifstream file;
file.open( filename );
if ( !file.is_open() ) {
// This ExceptionHandler will not work for you; you will need to supply your own error or exception handling.
std::ostringstream strStream;
strStream << __FUNCTION__ << " failed to read in file[" << filename << "]";
throw ExceptionHandler( strStream );
}
file >> numTestCases;
TowerCoordinate towerCoord;
std::vector<unsigned> penalties;
for ( unsigned t = 0; t < numTestCases; ++t ) {
file >> gridSize.width;
file >> gridSize.height;
file >> numTowers;
for ( unsigned c = 0; c < numTowers; ++c ) {
file >> towerCoord.x;
file >> towerCoord.y;
towerLocations.push_back( towerCoord );
}
unsigned currentPenalty = calculatePenalties( towerLocations, gridSize );
penalties.push_back( currentPenalty );
towerLocations.clear();
}
file.close();
return penalties;
}
int main() {
unsigned numTestCases = 0;
unsigned numTowers = 0;
GridSize gridSize;
std::vector<TowerCoordinate> towerLocations;
std::vector<unsigned> penalties;
penalties = getDefenderDataFromFile( "pieces.txt", numTestCases, gridSize, numTowers, towerLocations );
unsigned idx = 0;
for ( ; idx < penalties.size(); ++idx ) {
std::cout << penalties[idx] << " ";
}
std::cout << std::endl;
return 0;
}
对于这个算法的当前实现,唯一需要注意的是,在每个测试用例之后都会清除塔位置的向量,所以一旦你得到所有惩罚的最终结果,塔的容器就在当前坐标堆栈帧将仅包含最后一组位置,并且不会包含它们的任何先前迭代。再一次,没有根据网格大小对塔坐标进行边界检查。