题目描述
小 Z 最近捡到了一个棋盘,他想在棋盘上摆放 K 个皇后。他想知道在他摆完这 K 个皇后之后,棋盘上还有多少个格子是不会被攻击到的。
注意:一个皇后会攻击到这个皇后所在的那一行,那一列,以及两条对角线。
输入格式
第一行三个正整数 n,m,K,表示棋盘的行列,以及小 Z 摆放的皇后的个数。
接下来 K 行,每行两个正整数 x,y,表示这个皇后被摆在了第 x行,第 y 列,数据保证任何两个皇后都不会被摆在同一个格子里。
输出格式
仅一个整数,表示棋盘上还有多少个格子是不会被攻击到的。
输入输出样例
输入 #1
12 13 6
10 4
12 10
1 1
2 3
3 2
2 6
输出 #1
25
说明/提示
对于 30%的数据,1≤n,m≤5×10^3;1≤K≤500;
对于另外 10% 的数据,K=1;
对于 100% 的数据,1≤n,m≤2×10^4,1≤K≤500。
刚学会的对角线法,然后直接写的这个题,先全部标记,然后每个格子都搜索一遍,时间限制
可以把对格子的判断换为对一整行的判断,对每一行来说,所有坐标循环一遍,判断是否对当前行有影响(注意两个坐标可能会攻击到同一个地方)
先提前标记这行如果有坐标,则一定没有空
判断方法还是利用一次函数,判断X+Y(左下右上) ,X-Y+A(左上右下),是否在范围内(每个格子的这两个值都是一定的,所以一行内会有个范围)
为了避免重复攻击,用一个b数组,来记录相应的纵坐标
注意:如果一直用memset来更新数据,会时间限制,所以转化一下,让b储存他的行值即可
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int a[500005],b[500005];//这里的a标记了行,当前这行的列数,即答案
int total=0;
int n,xx[505],yy[505],m,k;
int main()
{
cin>>n>>m>>k;
for(int i=1;i<=k;i++)
{
cin>>xx[i]>>yy[i];
a[xx[i]]=1;
}
for(int i=1;i<=n;i++)
{
int t=m;
if(a[i]==1) continue;
else
{
//memset(b,0,sizeof(b));//标记格子是否被减过
int l1=i-1,r1=i-m;//第i行的,左上右下范围值
int l2=i+1,r2=i+m;//第i行的,左下右上范围值
for(int j=1;j<=k;j++)
{
if(b[yy[j]]!=i) t--,b[yy[j]]=i;//列的减一
if((xx[j]+yy[j]>=l2&&xx[j]+yy[j]<=r2)&&b[xx[j]+yy[j]-i]!=i) t--,b[xx[j]+yy[j]-i]=i;//左下右上
if((xx[j]-yy[j]>=r1)&&(xx[j]-yy[j]<=l1)&&b[yy[j]-xx[j]+i]!=i) t--,b[yy[j]-xx[j]+i]=i;//左上右下
}
total+=t;
}
}
cout<<total;
return 0;
}
每个格子都搜索一边,时间限制
O2优化后,也能通过
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
int a[500005],b[500005],c[500005],d[500005];//这里的a标记了行,当前这行的列数,即答案
int total=0;
int n,x,y,m,k;
void back(int x,int y)
{
a[x]=1;//行 占领
b[y]=1;//纵列 占领
c[x+y]=1;//左下右上
d[x-y+20000]=1;//因为不是正方形,x-y的值可能是负的
}
int main()
{
cin>>n>>m>>k;
while(k--)
{
cin>>x>>y;
back(x,y);
}
for(int i=1;i<=n;i++)
{
if(a[i]==1) continue;
for(int j=1;j<=m;j++)
{
if((!b[j])&&(!c[i+j])&&(!d[i-j+20000])) total++;
}
}
cout<<total;
return 0;
}