boj 407 第一次排位赛训练d bfs 找联通矩阵 最大正方形





其实今天我又回看了这道题,这题用bfs时间花销很大,内存也开得很大,而且也许当时我写得不够好,运行了300ms,所以今天我把题目仔细看了一遍,学习了别人的方法以后,发现这题可以用别的方法来做(bfs技术含量太高了,其实完全不需要,遍历就可以)

于是我找出了当时考试的时候我没有通过的代码,以此为模板进行修改,代码将粘贴在后面,时间缩减到了57ms,内存更是缩小了三分之二。这题主要考的是逻辑,算法其实没什么特殊的。是我自己想复杂了,把它和校赛的另外一题弄混了,在最后我将给出校赛的题,求最大完美1矩阵


http://code.bupt.edu.cn/problem/p/407/

bfs 会用到往四个方向找    注意可以这样写 这个在迷宫里也有用到

注意里面外面都是'{'号,不要写成'('   ;

int ah[2][2] = {(-1,0), (0,1)};  这样写是错误

int ah[4][2] = {{-1,0}, {0,1}, {1,0}, {0,-1}};

vis[j+ah[i][0]][k+ah[i][1]]

时间限制 1000 ms   内存限制 65536 KB

题目描述

给定一个 NM 的矩阵,求问里面有多少个由'#'组成的矩形,"There are 5 ships.",若是里面有一个不是矩形的联通块,则输出"So Sad"

输入格式

1n,m1000

有多组数据,EOF结束。

输出格式

每行对应一个answer

输入样例

6 8
.....#.#
##.....#
##.....#
.......#
#......#
#..#...#
6 8
.....#.#
##.....#
###...##
.......#
##.....#
#..#...#

输出样例

There are 5 ships.
So Sad

方法一  先要建一个sum数组,然后用bfs找连通块,找到以后再判断,是否是个矩形,不是直接退出,是的话记录访问过,继续寻找

 

 

#include <iostream>
#include <cstdio>
#include <string.h>
#include <vector>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;

int n, m;
char a[1005][1005];//储存字符
int b[1005][1005];//字符转化为数字,‘#’为1,其它为0
int vis[1005][1005];
int sum[1005][1005];//统计左上角到当前位置总数,用于判断一个矩形是否是全为1的矩形
queue <pair<int, int> > q;

int ans;//存放结果,符合条件矩形个数
int mini, minj, maxi, maxj;//一块连通区域的左上角与右下角(如果确实是符合条件的矩形的话)

void bfs()//广度搜索,找连通都是1的区域
{
    int ai[4] = {-1, 0, 1, 0};
    int aj[4] = {0, 1, 0, -1};
    while(!q.empty())
    {
        pair<int,int> tmp=q.front();
        q.pop();
        int j = tmp.first;
        int k = tmp.second;
        for (int i=0; i<4; i++)
        {
            if (((j+ai[i])<=0)||((j+ai[i])>n)||((k+aj[i])<=0)||((k+aj[i])>m)) continue;//越界
            else if (vis[j+ai[i]][k+aj[i]]) continue;//已被访问
            else if (!b[j+ai[i]][k+aj[i]])//为0,所以不符合,标记为访问过,跳过
            {
                vis[j+ai[i]][k+aj[i]] = 1;
                continue;
            }
            else//符合条件加入队列
            {
                vis[j+ai[i]][k+aj[i]] = 1;
                q.push(make_pair(j+ai[i], k+aj[i]));
                minj = min(k+aj[i], minj);
                maxj = max(k+aj[i], maxj);
                mini = min(j+ai[i], mini);
                maxi = max(j+ai[i], maxi);
            }
        }
    }
}


int main()
{
    while(~scanf("%d %d",&n,&m))
    {
        ans = 0;
        memset(a, 0, sizeof a);
        memset(b, 0, sizeof b);
        memset(sum, 0, sizeof sum);
        memset(vis, 0, sizeof vis);
        for (int j=0; j<n; j++)
        {
            scanf("%s", a[j]);
        }
        for (int j=1; j<=n; j++)
        {
            for (int k=1; k<=m; k++)
            {
                if (a[j-1][k-1]=='#')
                    b[j][k] = 1;
                else
                    b[j][k] = 0;
                sum[j][k] = sum[j-1][k]+sum[j][k-1]-sum[j-1][k-1]+b[j][k];
                //统计左上角到当前位置总数,用于判断一个矩形是否是全为1的矩形
            }
        }
        int flag = 0;
        for (int j=1; j<=n; j++)
        {
            for (int k=1; k<=m; k++)
            {
                if (vis[j][k]) continue;
                if (!b[j][k])
                {
                    vis[j][k] = 1;
                    continue;
                }
                while(!q.empty())
                    q.pop();
                q.push(make_pair(j, k));
                mini=maxi=j;
                minj=maxj=k;
                bfs();
                int temp = sum[maxi][maxj]-sum[mini-1][maxj]-sum[maxi][minj-1]+sum[mini-1][minj-1];
                //(mini, minj)到(maxi, maxj)的矩形如果都为1的值理想值与现实值的比较
                if (temp==(maxi-mini+1)*(maxj-minj+1))
                    ans++;
                else
                {
                    flag=1;
                    break;
                }
            }
            if (flag)
                break;
        }
        if (flag)
            printf("So Sad\n");
        else
            printf("There are %d ships.\n", ans);
    }
    return 0;
}


 

方法二 直接遍历

 

 

 

#include <iostream>
#include <cstdio>
#include <string.h>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;

int n, m;
char a[1005][1005];
int b[1005][1005];
bool vis[1005][1005];

int tempn, tempm;
int ans;

int cap(int j, int k)
{
    tempn=0, tempm=0;

    for (int q=k; q<=m; q++)
        if (b[j][q]&&b[j-1][q])//考虑上面有没有多余的#
            return 0;
        else if (b[j][q])
            tempm++;
        else break;

    for (int p=j; p<=n; p++)
    {
        if (b[p][k]&&b[p][k-1])//考虑左边有没有多余的#
            return 0;
        else if (!b[p][k]) break;//考虑是不是能往下推进一行
        else tempn++;
        for (int q=k+1; q<=k+tempm-1; q++)
        {
            if (!b[p][q]) return 0;//考虑推进的这一行是否整排都符合
        }
        if (b[p][k+tempm]) return 0;//考虑这一行的尾部是否多出了一个#,其实这里不要应该也可以吧,因为下次出现时会先考虑是否左边有多余的#

    }
    for (int q=k; q<=k+tempm-1; q++)//考虑末行的下一行是否有多余的#,当然这里也不用考虑,因为只要新出现一个#我们会考虑这一排的上面是否曾经出现过#
        if (b[j+tempn][q]) return 0;
    for (int p=j; p<=j+tempn-1; p++)
        for (int q=k; q<=k+tempm-1; q++)
            vis[p][q] = 1;
    ans++;
    return 1;
}
int main()
{
    while(~scanf("%d %d", &n, &m))
    {
        ans = 0;
        memset(a, 0, sizeof a);
        memset(vis, 0, sizeof vis);
        memset(b, 0, sizeof b);
        for (int j=0; j<n; j++)
            scanf("%s", a[j]);

        for (int j=1; j<=n; j++)
            for (int k=1; k<=m; k++)
                b[j][k] = a[j-1][k-1]=='#' ? 1 : 0;

        int flag = 1;
        for (int j=1; (j<=n)&&flag; j++)
            for (int k=1; (k<=m)&&flag; k++)
            {
                if (vis[j][k]||(!b[j][k])) continue;
                if (!cap(j, k))
                    flag = 0;
            }
        if (!flag)
            printf("So Sad\n");
        else
            printf("There are %d ships.\n", ans);
    }
    return 0;
}


校赛题

 http://code.bupt.edu.cn/problem/p/382/点击打开链接   这道题如果有时间还要好好研究一下,因为算法的复杂富

 

382. Largest Square

时间限制5000 ms 内存限制 65536 KB

题目描述

Give you a N*N( 1N2000 )Square.For each grid in the square,its color is red or black.Your task is to find a largest red square.

输入格式

The first line is an integer T ( 1T5 ) indicating the case number. For each case,the first line is N,then next N lines describes the square.If a_ij=0 ,indicating a_ij is black,if a_ij=1 ,indicating a_ij is red.

输出格式

For each case,output an integer which is the lenth of the side of the largest red square.

输入样例

2
3
101
000
111
3
111
111
111

输出样例

1
3





#include <algorithm>
#include<iostream>
#include<cstdio>
#include<string.h>
#include<vector>
 
using namespace std;
char a[2005][2005];
int sum[2005][2005];
int father[2005];
int maxt;
int l;
int flag;
void cap(int i, int j, int lt)
{
    int temp;
    int p;
    temp = sum[i][j]-sum[i-lt][j]-sum[i][j-lt]+sum[i-lt][j-lt];
    if (temp == lt*lt)//说明最大值大于等于l
    {
        flag = 1;
        maxt = (maxt>lt?maxt:lt);
        p = (father[lt]+lt)/2;
    }
    else if (!flag)
    {
        p = lt/2;
    }
    else
        p = (father[lt]+lt)/2;
 
    if (p == lt) return;
    father[p] = lt;
    cap(i, j, p);
 
}
int main()
{
    int num;
    scanf("%d", &num);
    for (int i=0; i<num; i++)
    {
        int N;
        scanf("%d", &N);
        memset(a, 0, sizeof a);
        memset(sum, 0, sizeof sum);
        //memset(value, 0, sizeof value);
        for (int j=0; j<N; j++)
        {
            scanf("%s", a[j]);
        }
        for (int j=1; j<=N; j++)
        {
            for (int k=1; k<=N; k++)
            {
                sum[j][k] = sum[j-1][k]+sum[j][k-1]-sum[j-1][k-1]+a[j-1][k-1]-'0';
            }
 
        }
        l = 0;
        maxt = 0;
        int temp;
        for (int j=1; j<=N; j++)
        {
            for (int k=1; k<=N; k++)
            {
                if (a[j-1][k-1]=='0') continue;
                memset(father, 0, sizeof father);
                flag = 0;
                l = (j<k?j:k);
                father[l] = l;
                cap(j, k, l);
            }
        }
        printf("%d\n", maxt);
    }
    return 0;
}

 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值