eoj 1021 用数组求解n皇后问题(输出冲突的皇后对数)

题目解析:给定一个n*n的棋盘,以及n个皇后,每个皇后的行、列、以及两条对角线的方向上,如果有其他皇后存在,则视为冲突。要求输出总冲突数。

思路1:

利用数组比较暴力地求解。设置一个棋盘规模为n*n
刚开始全部置为0;每放一个皇后则把与其冲突的地方全部+1;则每个新放的皇后的坐标对应的值即为冲突数

先粘上代码。

#include <iostream>

using namespace std;

/*
设置一个棋盘规模为n*n
刚开始全部置为0;每放一个皇后则把与其冲突的地方全部+1;则每个新放的皇后的坐标对应的值即为冲突数
*/

int main()
{
    int size; cin >> size;
    char* p = new char[size * size]{0};
    int conflictNum=0;
    for (int i = 0; i < size; i++)
    {
        int x, y; cin >> x >> y;
        x--; y--;
        conflictNum += p[x* size + y];
        //行上的坐标+1
        for (int j = 0; j < size; j++)
            p[x * size + j]++;
        //列上的坐标+1
        for (int q = 0; q < size; q++)
            p[q * size + y]++;
        //正对角线++
        for (int x1 = x, y1 = y; x1 < size && y1 < size; x1++, y1++)
            p[x1 * size + y1]++;
        for (int x1 = x, y1 = y; x1 >= 0 && y1 >= 0; x1--, y1--)
            p[x1 * size + y1]++;
        for (int x1 = x, y1 = y; x1 < size && y1 >= 0; x1++, y1--)
            p[x1 * size + y1]++;
        for (int x1 = x, y1 = y; x1 >= 0 && y1 < size; x1--, y1++)
            p[x1 * size + y1]++;
    }
    cout << conflictNum << endl;

    return 0;
}

用3*3的棋盘解释一下。

刚开始把棋盘全部设为0;

假设第一个皇后位于(1,1)这个点(皇后所在点会涂红),则(1,1)的行、列、对角线方向上的数全部+1;

假设第二个皇后位于(2,2)这个点,我们发现(2,2)这个点上的数为1,说明该点会与一个皇后发生冲突,故我们让总冲突数+1;同时把(2,2)的行、列、对角线上的点+1;

假设第三个皇后位于(3,3),(3,3)对应的值为2,说明该店会与两个皇后发生冲突,故让总冲突数+2,此时总冲突数为3;

最终输出结果为总冲突数是3;

对于规模为n的棋盘也同样适用。

于是在eoj提交了一下,结果是:

 Memory limit exceeded  (T_T)让我们红橙作伴活得潇潇洒洒~

不过其实也是预想当中的结果哈,怎么可能这么简单过呢~

题目有写,数据规模约定:

  • 对于 60% 的数据,1≤n≤100。
  • 对于 100% 的数据,1≤n≤105。

 对于10的5次方的情况,就需要开一个10的10次方规模的int数组了

那怎么办呢?

思路2:

不开辟棋盘,直接通过点与点之间的数学运算来判断是否在同一行、列or对角线上(可以看成记录一个稀疏数组)

若在同一行,则x相等

若在同一列,则y相等

若在同一对角线上则两点连线的斜率等于+1  or  -1;

粘上代码!

#include <iostream>

using namespace std;

class point
{
public:
	int x;
	int y;
	bool isconflict(int x1, int y1);
};
bool point::isconflict(int x1, int y1)
{
	if (x == x1)return true;
	else if (y == y1)return true;
	else if ((y1 - y) == (x1 - x) ) return true;
	else if ((y1 - y) == -1*(x1 - x)) return true;
	else return false;


}
int main()
{
	int size; cin >> size;
	point* arr = new point[size];
	int conflictnum = 0;
	for (int i = 0; i < size; i++)
	{
		cin >> arr[i].x >> arr[i].y;
		for (int j = 0; j < i; j++)
		{
			if (arr[i].isconflict(arr[j].x, arr[j].y))
				conflictnum++;
		}
	}
	cout << conflictnum << endl;
	return 0;
}

结果捏,结果是……Time limit exceeded

不是空间复杂度超了就是时间复杂度超了……泪目

那么,到底怎么样才能AC呢? 

 思路3:(最终成功AC)

假设是一个n*n的数组,每读入一个皇后的坐标,读取该皇后所在的行、列、两条对角线已有的皇后数,所在行、列、对角线的皇后数加起来即为会与该(新读入的)皇后发生冲突的皇后数,然后对所在行、列、对角线的皇后数+1;

需要开四个数组,空间复杂度为n

开一个规模为n的数组记录每一行现有的皇后数量

一个规模为n的数组记录每一列现有的皇后数量

两个规模为2n-1的数组记录正对角线和副对角线上现有的皇后数量;

先粘上我的代码~

#include <iostream>

using namespace std;

/*
记录每一行每一列每一对角线的皇后数

*/

int main()
{
	int size; cin >> size;
	//行数组
	int* line = new int[size]{0};
	//列数组
	int* col = new int[size]{0};
	//主对角线数组
	int* mainDaig = new int[2 * size - 1]{0};
	//副对角线数组
	int* deputyDaig = new int[2 * size - 1]{0};
	long long conflictNum = 0;
	for (int i = 0; i < size; i++)
	{
		int x, y;
		cin >> x >> y;
		x--;
		y--;
		conflictNum =conflictNum+ line[x] + col[y] + mainDaig[y - x +size-1] + deputyDaig[2*size-2-x-y];
		line[x]++;
		col[y]++;
		mainDaig[y - x + size-1]++;
		deputyDaig[2*size-2-x-y]++;

	}
	cout << conflictNum << endl;
	
	return 0;
}

用一个3x3的棋盘举例。

对于一个3x3的棋盘,有三行、三列、五条正对角线、五条副对角线。(五条正、副对角线如图,绿色为正对角线,蓝色为副对角线)

 则我们对应的开四个数组,并且把四个数组中的元素全部置为0,表示每一行、每一列、每一条对角线上都还没有元素。(因为现在还没开始往棋盘上放皇后棋子)

//行数组
	int* line = new int[size]{0};
	//列数组
	int* col = new int[size]{0};
	//主对角线数组
	int* mainDaig = new int[2 * size - 1]{0};
	//副对角线数组
	int* deputyDaig = new int[2 * size - 1]{0};

开始放皇后,假设第一个皇后坐标为(1,1)

ps:题上的坐标xy都是大于等于1小于等于n的,其实严谨来说我们认为题上的(1,1)等同于计算机语言中的(0,0)

这个皇后位于第0行、第0列、第2条主对角线、第4条副对角线上;而此时这些线上都没有皇后会与她冲突(当然啦因为现在是放的第一个棋子,怎么可能冲突呢)所以冲突总数仍是0

conflictNum =conflictNum+ line[x] + col[y] + mainDaig[y - x +size-1] + deputyDaig[2*size-2-x-y];

放上去之后第0行、第0列、第2条主对角线、第4条副对角线上都有了一个皇后,我们就让对应的数组的值+1;

        line[x]++;
		col[y]++;
		mainDaig[y - x + size-1]++;
		deputyDaig[2*size-2-x-y]++;

(或许有人会疑惑为什么是第0行,为什么是第2条对角线。我画了个图希望能帮助大家理解我诡异的坐标系和编号方式)

 以此类推,我们每放一个棋子都如此操作,先读取该棋子所在行、列、对角线上已有的皇后的个数(即与该棋子发生冲突的棋子数),分别加入到总冲突数中,然后使得对应行、列、对角线的皇后个数都+1。

最后输出总冲突数即可。

这里面还有一个数学问题,怎么由坐标获得对应的行标、列标、对角线标呢?这就是一个数学问题啦。行标等于x,列标=y,而对角线标的表达方式若是看不出来,可以通过求解方程的方式获得。

按照我的标号方式有:

主对角线标=y-x+size-1;

副对角线标=2*size-2-x-y;

如果不能观察出表达式则可以通过设方程为

Ax+By+C=对角线标;

代入三组x,y,对角线标即可解得ABC,即获得用x,y,size表示的对角线标的表达式。

注意:总冲突数如果用int存储,当数据为十万个时,可能会发生越界,所以我们用long long来存储。

贴上我的快乐AC图!

 第一次写blog比较粗糙,不过希望能对大家有所帮助嗷!!

猜你喜欢

转载自blog.csdn.net/weixin_61720360/article/details/123569871
今日推荐