题目解析:给定一个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比较粗糙,不过希望能对大家有所帮助嗷!!