前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >洛谷P1331 海战 题解

洛谷P1331 海战 题解

作者头像
yzxoi
发布2022-09-19 12:25:50
2270
发布2022-09-19 12:25:50
举报
文章被收录于专栏:OI

洛谷P1331 海战 题解

题目传送门

思路

肯定食用dfs啦。。。

但关键是两条船接触了怎么判断呢??

上图:

可以发现一下规律

当两条船接触时,必有一条直线连续穿过两条船

当一条船不与另一条船接触时,没有一条直线连续穿过两条船

所以只需要在每一次碰见一条船的一部分(一条船内每个点都要拓展一遍)时,将其沿右上、左下分别拓展一遍,边拓展边用sum前缀和check一遍就好啦。。。。

核心代码:

代码语言:javascript
复制
//码风很丑,勿喷
int k=1;
while(a[i+k][j+k]==1) k++;//找到最左下的一个点(即连线段的另一个端点)
--k;//别忘了再加回来
int hh=sum[i+k][j+k]-sum[i-1][j]-sum[i][j-1]+sum[i-1][j-1];//前缀和
++k;
if(hh<k*k){//前缀和必须是k*k(一个正方形)
    puts("Bad placement.");
    return 0;//强制结束
}//沿右上
k=1;//清零
while(a[i+k][j-k]==1) ++k;//找到最左下的一个点(即连线段的另一个端点)
--k;
hh=sum[i+k][j]-sum[i-1][j]-sum[i+k][j-k-1]+sum[i-1][j-k-1];//前缀和
++k;//别忘了再加回来
if(hh<k*k){//前缀和必须是k*k(一个正方形)
    puts("Bad placement.");
    return 0;//强制结束
}//沿左下

如果没有接触的话,可以直接dfs啦。。。代码简洁:

代码语言:javascript
复制
void dfs(int x,int y){
    if(vis[x][y]==1) return ;//如果已经拓展过直接退出
    vis[x][y]=1;//标记
    if(a[x][y+1]==1) dfs(x,y+1);//向四个方向拓展,标记为此船
    if(a[x-1][y]==1) dfs(x-1,y);
    if(a[x+1][y]==1) dfs(x+1,y);
    if(a[x][y-1]==1) dfs(x,y-1);
}

所有代码:(非常“简洁”。。。)

代码语言:javascript
复制
#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int ret=0,f=1;char ch=getchar();
    while (ch<'0'ch>'9') {if (ch=='-') f=-f;ch=getchar();}
    while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    return ret*f;
}//丑陋的读优
void write(int x){
    if(x<0){
        putchar('-');
        write(-x);
        return ;
    }
    if(x<10) putchar(x+'0');
    else{
        write(x/10);
        putchar(x%10+'0');
    }
}//丑陋的输优
int n,m,a[1010][1010],sum[1010][1010];
int vis[1010][1010],ans;
void dfs(int x,int y){
    if(vis[x][y]==1) return ;
    vis[x][y]=1;
    if(a[x][y+1]==1) dfs(x,y+1);
    if(a[x-1][y]==1) dfs(x-1,y);
    if(a[x+1][y]==1) dfs(x+1,y);
    if(a[x][y-1]==1) dfs(x,y-1);
}//丑陋的dfs
int main(){
    n=read();m=read();
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            char ch;cin>>ch;
            if(ch=='#') a[i][j]=1;
        }
    }//读入
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
        }
    }//前缀和
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(a[i][j]==1){
                int k=1;
                while(a[i+k][j+k]==1) k++;
                --k;
                int hh=sum[i+k][j+k]-sum[i-1][j]-sum[i][j-1]+sum[i-1][j-1];
                ++k;
                if(hh<k*k){
                    puts("Bad placement.");
                    return 0;
                }
                k=1;
                while(a[i+k][j-k]==1) ++k;
                --k;
                hh=sum[i+k][j]-sum[i-1][j]-sum[i+k][j-k-1]+sum[i-1][j-k-1];
                ++k;
                if(hh<k*k){
                    puts("Bad placement.");
                    return 0;
                }
            }//不想再提了。。。判断船只是否接触
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(a[i][j]==1&&vis[i][j]==0){
                ++ans;
                dfs(i,j);
            }//dfs统计答案
        }
    }
    cout<<"There are ";
    write(ans);
    cout<<" ships.";putchar('\n');//输出
    return 0;//结束。。。。
}
/*************************
用时: 102ms / 内存: 5152KB
代码:1.63KB C++
By yuzhengxi
**************************/
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-02-15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 洛谷P1331 海战 题解
  • 题目传送门
  • 思路
    • 肯定食用dfs啦。。。
      • 但关键是两条船接触了怎么判断呢??
        • 上图:
        • 可以发现一下规律
        • 当两条船接触时,必有一条直线连续穿过两条船
        • 当一条船不与另一条船接触时,没有一条直线连续穿过两条船
      • 所以只需要在每一次碰见一条船的一部分(一条船内每个点都要拓展一遍)时,将其沿右上、左下分别拓展一遍,边拓展边用sum前缀和check一遍就好啦。。。。
        • 核心代码:
        • 如果没有接触的话,可以直接dfs啦。。。代码简洁:
        • 所有代码:(非常“简洁”。。。)
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档