其实今天我又回看了这道题,这题用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]]
407. BLOCKS
题目描述
给定一个 N∗M 的矩阵,求问里面有多少个由'#'组成的矩形,"There are 5 ships.",若是里面有一个不是矩形的联通块,则输出"So Sad"
输入格式
1≤n,m≤1000
有多组数据,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
题目描述
Give you a N*N( 1≤N≤2000 )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 ( 1≤T≤5 ) 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;
}