BITMAP-Bitmap 题解

最近总喜欢在SPOJ上刷黑白双煞题

题目传送门

这题的思路异常简单。实际上,如果你稍微看一看,就会发现……

范围:1<=n,m<=182\color{CornflowerBlue}\text{范围:1<=n,m<=182}

所以很多人第一反应想到的就是暴力搜索啊!

  • 只需要输入+搜索就可以搞定啊!

想法很完美,实际很崩溃\color{Green}\text{想法很完美,实际很崩溃}

虽然我没试过,但是大概算一下,搞不好最坏要:T 组数据 · 矩阵大小 · 矩阵大小 (枚举0、1两格)

矩阵长和宽为182的范围感觉还是吃不消的。

所以就需要进一步优化。

其实具体也还是很简单的:

  • 既然我们枚举的是0、1两格的位置……

  • 不妨直接记录下来,岂不美哉?\color{CornflowerBlue}\text{不妨直接记录下来,岂不美哉?}

具体记录也很简单,输入时加个 if-else 就可以了。如果怕空间浪费的话,可以用 STL 的 vector ,动态延长数组,在此基础上的访问和末尾添加元素也和数组一样是 O(1) 的。


回到正题,如果记录下来了每个0和1的位置,就只需要枚举位置就行了。大概可以优化到 O(T * n2^{2})。(T是数据组数)

最后乘上数据数量,虽然 T 的范围我没有找到,但实测得出这是不会TLE的。

或许就可以打出代码了……

#include<bits/stdc++.h>
using namespace std;
int T,n,m,i,j,b[190][190],s;
bool a[190][190];
char x;
vector<int> z[2];
vector<int> w[2];
int main(){
   ios::sync_with_stdio(false);
   cin.tie(0);
   cout.tie(0);
   cin>>T;
   while(T-->0){
   	cin>>n>>m;
   	for(i=1;i<=n;i++) for(j=1;j<=m;j++){
   		cin>>x;
   		if(x=='0') a[i][j]=0;
   		else a[i][j]=1;
   		if(!a[i][j]){
   			z[0].push_back(i);
   			z[1].push_back(j);
   		}
   		else{
   			w[0].push_back(i);
   			w[1].push_back(j);
   		}
   	}
   	for(i=0;i<z[0].size();i++){
   		s=2147483647;
   		for(j=0;j<w[0].size();j++) s=min(s,abs(z[0][i]-w[0][j])+abs(z[1][i]-w[1][j]));
   		b[z[0][i]][z[1][i]]=s;
   	}
   	for(i=1;i<=n;i++){
   		for(j=1;j<=m;j++) cout<<b[i][j]<<" ";
   		cout<<"\n";
   	}
   }
   return 0;
}

但是这个代码一交,就会发现……0分\color{red}\text{0分}……

这里大家一定要谨慎,因为多组数据的题目是要初始化的!

#include<bits/stdc++.h>
using namespace std;
int T,n,m,i,j,b[190][190],s;
bool a[190][190];
char x;
vector<int> z[2];
vector<int> w[2];
int main(){
   ios::sync_with_stdio(false);
   cin.tie(0);
   cout.tie(0);
   cin>>T;
   while(T-->0){
   	memset(a,0,sizeof(a));
   	memset(b,0,sizeof(b));
   	z[0].clear();
   	z[1].clear();
   	w[0].clear();
   	w[1].clear();//初始化
   	cin>>n>>m;
   	for(i=1;i<=n;i++) for(j=1;j<=m;j++){
   		cin>>x;
   		if(x=='0') a[i][j]=0;
   		else a[i][j]=1;
   		if(!a[i][j]){
   			z[0].push_back(i);
   			z[1].push_back(j);
   		}
   		else{
   			w[0].push_back(i);
   			w[1].push_back(j);
   		}
   	}
   	for(i=0;i<z[0].size();i++){
   		s=2147483647;
   		for(j=0;j<w[0].size();j++) s=min(s,abs(z[0][i]-w[0][j])+abs(z[1][i]-w[1][j]));
   		b[z[0][i]][z[1][i]]=s;
   	}
   	for(i=1;i<=n;i++){
   		for(j=1;j<=m;j++) cout<<b[i][j]<<" ";
   		cout<<"\n";
   	}
   }
   return 0;
}

最终时间上优化差不多啦,现在看看有没有空间优化。

除了用 vector 以外,我们发现了一个无用的 a 数组。这个数组 全程只用过一次,而且没有意义\color{Blue}\text{全程只用过一次,而且没有意义},可以去掉。

最终出现了这个空间优化版\color{Goldenrod}\text{空间优化版}

#include<bits/stdc++.h>
using namespace std;
int T,n,m,i,j,b[190][190],s;
//原先存矩阵的 a 数组被优化掉了
char x;
vector<int> z[2];
vector<int> w[2];
/*
* 第一维->横坐标
* 第二维->纵坐标
*/
int main(){
   ios::sync_with_stdio(false);
   cin.tie(0);
   cout.tie(0);
  // 以上是输入输出优化的代码
   cin>>T;
   while(T-->0){
   	memset(b,0,sizeof(b));
   	z[0].clear();
   	z[1].clear();
   	w[0].clear();
   	w[1].clear();
       
// ------初始化完毕-------- //

   	cin>>n>>m;
   	for(i=1;i<=n;i++) for(j=1;j<=m;j++){
   		cin>>x;
   		if(x=='0'){
   			z[0].push_back(i);
   			z[1].push_back(j);
   		}
   		else{
   			w[0].push_back(i);
   			w[1].push_back(j);
   		}
       // 注意是字符串读入
   	}
   	for(i=0;i<z[0].size();i++){
   		s=2147483647;
               // 这是int范围最大值
   		for(j=0;j<w[0].size();j++) s=min(s,abs(z[0][i]-w[0][j])+abs(z[1][i]-w[1][j])); // 这是题目里给出的坐标公式,注意取绝对值,不然有可能得到个负数
   		b[z[0][i]][z[1][i]]=s;
           // 存储答案
   	}
   	for(i=1;i<=n;i++){
   		for(j=1;j<=m;j++) cout<<b[i][j]<<" ";
   		cout<<"\n";
   	}
       // 以上是输出,不用讲吧~
   }
   return 0;
}

以上题解中出现的代码本蒟蒻都已经交过并测试了,这里是我的提交记录,已经加入代码公开计划了~