什么是防御王国的好算法?

时间:2011-02-18 08:36:26

标签: c++ algorithm

我试图解决Defense of a Kingdom problem并提出算法,但它超出了问题的时间限制。

我想知道一个好的算法,可以在限定时间内解决这个问题。

问题:

  

Theodore实施了一个新的战略游戏“防御王国”。上   每个级别一个玩家为一个由a代表的王国进行辩护   矩形网格单元格。玩家在某些建造弩塔   网格的细胞。塔保卫同一排中的所有细胞   同一列。没有两座塔共用一排或一列。

     

位置的惩罚是最大的细胞数   不设防的矩形。例如,图片上显示的位置   罚款12。

     

     

帮助西奥多写一个计算给定位置惩罚的程序。

     

输入

     

输入文件的第一行包含测试用例的数量。

     

每个测试用例由一个包含三个整数的行组成:w -   网格的宽度,h - 网格的高度和n - 弩的数量   塔(1≤w,h≤40000;0≤n≤min(w,h))。

     

以下n行中的每一行包含两个整数xi和yi -   塔所占据的单元坐标(1≤xi≤w;1≤yi≤   小时)。

     

输出

     

对于每个测试用例,输出一个整数 - 数字   最大的矩形中的单元格,不受塔楼的保护。

     

实施例

     

输入:
  1
  15 8 3
  3 8
  11 2
  8 6

     

输出:12

7 个答案:

答案 0 :(得分:6)

我会这样做:

给定wh作为比赛场地的宽度和高度,以及塔的坐标为(x1,y1) .. (xN,yN),将坐标分成两个列表x1..xN,{ {1}},对这两个坐标列表进行排序。

然后计算空白空间,例如y1..yN。对y坐标执行相同的操作:dx[] = { x1, x2-x1, ..., xN - xN-1, (w + 1) - xN }。将dy[] = { y1, y2-y1, ..., yN - yN-1, (h + 1) - yN }乘以max(dx)-1,您应该拥有最大的未覆盖矩形。您必须将delta值减1,因为高坐标塔所覆盖的线包含在其中,但未被覆盖。

答案 1 :(得分:2)

很容易看出,一组不设防的细胞在水平墙壁上是cartesian product不设防的“洞”。因此,首先,您不需要将整个字段存储在内存中 - 只存储两个塔的坐标序列就足够了。

第二个观察结果是,在最终场地中,所有塔都设置好,最大的不设防矩形等于两个最宽壁洞的笛卡尔积。因此其面积等于孔长度的乘积。所以我们真正需要做的是找到两个最宽的墙洞(一个在x轴上,一个在y上),并将它们的长度相乘。那就是答案。

最后一点是关于输入的。塔楼可能会以某种方式改组;但是我们需要一种方法来获得所有孔的长度。这可以通过首先分别对一个和另一个坐标序列进行排序,然后计算{x i + 1 -x i }和 单次传递中{y i + 1 -y i }。在同一通行证中,我们甚至可以找到最大值 - 乘以它们,然后就完成了。

答案 2 :(得分:0)

好的,这可能是另一个想法,

对于每个防守者,至少有1个,最多4个邻居白色区域。

  1. a:=0
  2. 对于每个后卫,从...开始     最大的相邻白色区域。从那里走到     一个相邻的裸露空间     更大的区域,你没有移动     之前。这样做直到没有可能的举动。
  3. 如果没有可能的移动且当前区域大于a存储当前区域             a

答案 3 :(得分:0)

如果您有9x6网格。你有3座塔。

首先计算x轴的最小间隙,该轴具有9个元素。我们有3座塔楼。 9/3 = 3.所以我们每3个元素放置一个塔。

[ ]
[ ]
[x]
[ ]
[ ]
[x]
[ ]
[ ]
[x]

最多2个差距。我们可以通过用塔数(3)潜水剩余空间(6)来解决这个问题。 6/3 = 2。

现在y轴相同。 6个方格。 3座塔楼。 6/3 =每2个方格一个塔:

[ ][x][ ][x][ ][x]

1个最大间隙(3/3)。

您现在拥有每个塔的x和y坐标(0索引):

1,2
3,5
5,8

最大差距是2x1 = 2.

[ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ]
[ ][x][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ]
[ ][ ][ ][x][ ][ ]
[ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][x]

我99%确定你可以为此创建一个通用公式,而不需要返回每个城堡的x,y对和最大惩罚区域的循环。

答案 4 :(得分:0)

编辑我确实注意到这个程序中有一个小错误: - 当我最初提交它时,它只针对一个测试用例;然后我回到我的IDE来改进算法以适用于多个测试用例。在我开始工作之后,我回到这里编辑这篇文章,我错过了几条关键线。它们现在已经修复,我还添加了一些注释,如果想要保留每个测试用例的相关记录及其相应的惩罚值,还可以将哪些内容添加到此类中。 结束修改

我把它封装成一个类。我非常确定这可能是一种更优雅的方式,但这就是我所提出的,使用您的文本文件示例来填充数据结构和返回处罚的方法。

<强> 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方向上最大差异的乘积,这将给你最大的开放区域。

我还注意到代码中存在某种类型的重复,它也可以通过向类中添加几个较小的函数来重构,但是为了显示算法我不认为它真的需要在这个案例。此算法唯一的双循环是从文件中读取数据。实际计算从坐标对的线性向量起作用。有3个单循环遍历向量。前两个在x方向上是相同的一次,在y方向上是一次。所以时间是基于输入N的大小或长度。这应该是恒定的时间。最后一个for循环仍然是N但是查看大小为N + 1的向量,该向量仍然是恒定时间,其中N是塔或坐标对的数量。折衷的是由于本地向量存储x和x中的差异,因此存在一点内存开销。 y值以及对于小型和中等数据集,这不应该是一个问题。

<强>声明

在我的回答评论中提到了这一点:

  

感谢您的努力,如果这是消极的,那就很抱歉...但我想知道人们什么时候会教这种类型的&#34;封装&#34;进入&#34;班级&#34;作为面向对象课程的一部分......这可能是我个人的品味,但是私人成员的职能并没有争论并且回归虚空对我来说是一个红旗。在阅读代码时,必须跟踪可能被修改的所有成员与在编程的黑暗时代中拥有全局变量一样糟糕。在这种特殊情况下,它们有助于隐藏第二个测试用例同时使用测试用例1和2中的塔的错误

是的,代码中有一个错误,我已经修复了如上所述。现在,如果查看此类对象或OO方法的整体设计,他们将看到该实现对此类对象的用户或调用者是隐藏的。他在这里发生的事情有一个特定的顺序。

步骤中的算法

  • 使用用户定义的构造函数声明此类型的对象。
  • 构造函数采用字符串作为文件名来读取数据。
  • 该文件已打开,如果成功则开始读取数据,否则会引发异常。
  • 它找出了有多少个测试用例,并且对于每个测试用例,它执行以下操作:
    • 检索塔的数量。
    • 获取此测试用例中每个塔的坐标位置。
    • 计算此测试用例的所有塔的惩罚。
    • 存储此案例的惩罚值。
    • 对所有测试用例重复此过程。
    • 关闭文件流。
    • 完成对象的构建。
  • 现在对象已经完成,唯一剩下的就是让用户调用其公共接口方法来检索惩罚值。

其他功能 - 可以添加这些功能 -

  • 根据网格MxN大小检查塔架坐标。
  • 所有案件的统计数据 - 重复,平均,中位数,模式,标准差等
  • 内置印刷方法。
  • 附加容器,用于存储所有塔单元位置以及每个测试用例的相关惩罚值。
  • 更多错误检查。
  • 一些重构&amp;提高效率。

修订 lisyarus在评论中提出了一个很好的观点,并且基于这里是非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;
}

此算法的当前实现唯一需要注意的是,在每个测试用例之后,塔位置的向量将被清除,因此一旦您获得了所有惩罚的最终结果,当前塔式电容器的容器就会被清除堆栈帧将仅包含最后一组位置,并且不包含它们的任何先前迭代。再一次,没有界限检查塔架坐标与网格尺寸。

答案 5 :(得分:0)

这是象棋和白棋问题的一种变体。

只需考虑y轴maxY_by_adjacent上坐标的最大相邻间隙。

只需考虑X轴maxX_by_adjacent上坐标的最大相邻间隙。

要查找相邻的间隙,请先对其进行排序。

这将使最长的未定界矩形width=maxX_by_adjacent & height=maxY_by_adjacent。 现在,找出此矩形maxX_by_adjacent * maxY_by_adjacent上的像元数。

int h,w,n;
scanf("%d %d %d",&w,&h,&n);
int x[n+2];
int y[n+2];
for(int i=1;i<=n;i++)
{
    scanf("%d%d",&x[i],&y[i]);
}
x[0] = 0;
x[n+1] = w + 1;
y[0] = 0;
y[n+1] = h + 1;
sort(x,x+n+1);
sort(y,y+n+1);
int maxX_by_adjacent=0;
int maxY_by_adjacent=0;
for(int i=1;i<=n+1;i++)
{
    maxX_by_adjacent=max(maxX_by_adjacent,x[i]-x[i-1]-1);
}
for(int i=1;i<=n+1;i++)
{
    maxY_by_adjacent=max(maxY_by_adjacent,y[i]-y[i-1]-1);
}
int ans=maxX_by_adjacent*maxY_by_adjacent;
printf("%d\n",ans);

答案 6 :(得分:0)

您可以使用贪婪方法来解决此问题,我们的目的是找到最大的无限制矩形。矩形面积=行数*列数。我们必须最大化这两个参数。要找到最大列数矩形,将墙壁的x坐标排序,取两个连续x坐标之间的差。此差的最大值将是最终矩形中的最大列数。进行同样的操作以查找最后一个矩形中的行数。请参考我的代码。https://github.com/sahazeer123/SPOJ/blob/master/DEFKING.cpp