P2105 K皇后

题目描述
小 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值