图论问题(1) : hdu 1198

 ̄綄美尐妖づ 提交于 2019-12-06 15:18:27

题目转自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掉,所以就花了更多时间去肝最优解(结果发现没啥卵用!)

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!