题目转自hdu 1198,题目传送门
题目大意:
给你11种单位水管摆放位置,若上下或左右有水管连接则视为这两点相连。
最后让你求这些张图中有几个连通块。
本来觉得这道题很简单,不就一个建图在并查集不就完了吗?
(如果连并查集都不会,请点此学习一下)
当然,并没有这么简单(在T了十几次后终于明白)
然后,我们可以发现,每个单位只有4个方向。
很容易想到用二进制进行状态压缩
压缩代码如下:
int change(char ch) { switch(ch) { case 'A':return 9; case 'B':return 12; case 'C':return 3; case 'D':return 6; case 'E':return 10; case 'F':return 5; case 'G':return 13; case 'H':return 11; case 'I':return 7; case 'J':return 14; case 'K':return 15; } }
然后写一个并查集模板就应该可以AC了(本人没试过)
可能是因为T太多回了,所以产生了心理阴影
于是,本人不厌其烦地去写时间复杂度更低的算法
在思考了近10min后,我终于有点头绪了
提到二进制,我最先想到的就是位运算
我就想,这道题能不能用位运算优化呢?
实际上是可以的,在思考了15min后我认可了这种想法
接着我用不到10min的时间肝了一段用位运算优化建图过程的代码
优化核心代码如下:
if(i>0&&((e[i][j]>>3)&(e[i-1][j]>>1)&1)) Union(i*m+j,(i-1)*m+j); if(j>0&&((e[i][j-1]>>2)&e[i][j]&1)) Union(i*m+j,i*m+j-1);
p.s.:中间的Union是并查集中的合并
然后,又随便写了10min的并查集模板,就愉快地AC了
AC代码:
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; char e[50][50]; int fa[2500]; bool vis[2500]; int ans,n,m; int Init() { for(int i=(n+1)*(m+1);i>=0;i--) fa[i]=i; } int change(char ch) { switch(ch) { case 'A':return 9; case 'B':return 12; case 'C':return 3; case 'D':return 6; case 'E':return 10; case 'F':return 5; case 'G':return 13; case 'H':return 11; case 'I':return 7; case 'J':return 14; case 'K':return 15; } } int find(int x) { if(fa[x]==x) return x; else { fa[x]=find(fa[x]); return fa[x]; } } void Union(int x,int y) { int tmp_x=find(x),tmp_y=find(y); fa[tmp_y]=tmp_x; return; } int main() { while(~scanf("%d%d",&n,&m)&&n!=-1) { getchar(); Init(); for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { scanf("%c",&e[i][j]); e[i][j]=change(e[i][j]); if(i>0&&((e[i][j]>>3)&(e[i-1][j]>>1)&1)) Union(i*m+j,(i-1)*m+j); if(j>0&&((e[i][j-1]>>2)&e[i][j]&1)) Union(i*m+j,i*m+j-1); } getchar(); } ans=0; memset(vis,0,sizeof(vis)); for(int i=0;i<n;i++) for(int j=0;j<m;j++) if(!vis[find(i*m+j)]) { vis[find(i*m+j)]=true; ans++; } cout<<ans<<endl; } return 0; }
emmmm......
提交之后我发现才14ms......(早知道我就不写位运算优化了)
这道题告诉我们,T了不可怕
可怕的是你害怕T掉,所以就花了更多时间去肝最优解(结果发现没啥卵用!)