首页 > 其他分享 >hdu 4012(bfs+位压缩)

hdu 4012(bfs+位压缩)

时间:2023-05-29 19:37:53浏览次数:40  
标签:hdu cur 4012 入队 len bfs flag que now




思路:每次涂色以后必有一个格子的颜色是最终的颜色,否则这次涂色根本没意义,所以我们把每次涂色后哪些格子成为最终颜色的所有可能都入队,入队的元素是一个struct包含步数和最终颜色已确定的木块集合,这个集合必须用整数表示,所以用到状态压缩,因为最多只有16个格子,所以用16位的二进制来表示,1,表示此格子已是最终颜色,0,表示仍待涂色。

这道题目确实很难想,我最开始想的是用字符串哈希把每次涂的情况存下来,结果挂了。。。其实这样的做法并不能保证就一定是最少的步骤,因为涂的情况不同,但最终都能够到达最终的状态。。所以并不见得就一定是最优的。。

这题至少让我知道了如何找位压缩后的子集。。。详见代码


#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;

struct node
{
    int step,cur;
};
int len;
bool flag[1<<16]; //  记录哪些格子已是最终颜色
char str[20];

int bfs()
{
    memset(flag,0,sizeof(flag));
    queue<node> que;
    node start={0,0};
    que.push(start);
    flag[0]=true;
    while(!que.empty()){
        node now=que.front();
        que.pop();
        if(now.cur==(1<<2*len)-1) return now.step; //颜色已涂满所有格子
        node next;
        next.step=now.step + 1;
        for(int i = 0;i < 2*len; i++){  //遍历这个图,找个起点开始涂色
            int tmp=0;
            if((1<<i) & now.cur) continue; //这个点已是最终颜色,continue
            for(int j = i;j < (i/len+1)*len; j++){  //单行向右扩展,上界的确定是技巧
                if((1<<j)&now.cur) break;       //已扩展到最终颜色,不能把它覆盖,扩展结束
                if(str[j]==str[i]) tmp|=(1<<j); //如果拓展的位置需涂的颜色和起点颜色一样,那么就涂
            }
            for(int j = i-1;j >= (i/len)*len; j--){ //单行向左扩展,下界的确定是技巧
                if((1<<j) & now.cur) break;   //已扩展到最终颜色,不能把它覆盖,扩展结束
                if(str[j] == str[i]) tmp|=(1<<j);//如果拓展的位置需涂的颜色和起点颜色一样,那么就涂
            }
            for(int j = tmp; j > 0; j = tmp & (j-1)){ 
    //把本次单行扩展的所有格子最终哪些格子成为最终颜色的所有可能入队,所有
    //可能也就是子集的所有可能,这种遍历集合子集的方式属于位压缩的知识
                if(!flag[j|now.cur]){  //这种情况已经产生过就不需入队了
                    flag[j|now.cur]=1;
                    next.cur=(j|now.cur); 
                    que.push(next);
                }
            }
            if(i >= len) continue;  //双行扩展,只要对某一行的点遍历作为起点即可
            if((1<<(i+len)) & now.cur) continue; //易错,这个起点的另一行对应的起点如果已是最终颜色,continue
            tmp=0; 
            for(int j = i;j < len; j++){  //和单行拓展类似
                if((1<<j) & now.cur)break;
                if((1<<(j+len)) & now.cur)break;
                if(str[j] == str[i]) tmp|=(1<<j);
                if(str[j+len] == str[i]) tmp|=(1<<(j+len));
            }
            for(int j = i-1; j >= 0; j--){
                if((1<<j) & now.cur) break;
                if((1<<(j+len)) & now.cur) break;
                if(str[j] == str[i]) tmp|=(1<<j);
                if(str[j+len] == str[i]) tmp|=(1<<(j+len));
            }
            for(int j = tmp; j > 0; j = tmp & (j-1)){ //和单行拓展子集入队类似
                if(!flag[j|now.cur]){
                    flag[j|now.cur]=1;
                    next.cur=(j|now.cur);
                    que.push(next);
                }
            }
        }
    }
}
int main()
{
    int t,cas = 1;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&len);
        scanf("%s%s",str,str+len);
        printf("Case #%d: %d\n",cas++,bfs());
    }
    return 0;
}




标签:hdu,cur,4012,入队,len,bfs,flag,que,now
From: https://blog.51cto.com/u_16143128/6373649

相关文章

  • hdu 1593(数学)
    往相反的方面跑,但是,最理想的初始位置并不是圆点和圆上的某一点,应该还有更理想的初始逃跑状态.这里有一点需要注意,就是逃跑者极力想达到理想逃跑初态,而追赶者极力阻止逃跑者达到这一状态,所以,理想初态应该是无论追赶者如何阻止,逃跑者仍然可以达到的理想状态.最理想的......
  • poj 2415(BFS)
    题意: 有一种游戏,共有三个piece(不妨称为棋子),棋盘是由N个点构成的完全图,边有颜色。每次可以移动一个棋子,移动时必须满足棋子走过的边的颜色和其他两个棋子之间的连边的颜色一致。求把三个棋子移到同一个顶点的最少次数。这道题是一个很简单的BFS,但为何一直TLE。。。。#include<ios......
  • hdu 3577(线段树区间更新)
    题意:输入一个t,表示有t组测试数据;         接下来一行,输入两个数,k,m,其中k表示这个辆车最多可以坐这么多人,m表示有m次询问能否上车;         每一次询问,输入两个数a,b,表示该乘客能否在a站台上车,b站台下车,乘车区间为(a,b--),先后次序;         即我每次询......
  • hdu 3172(并查集+hash)
    解题思路:典型的并查集,只是每个人的名字要转换成数字,可以用map,也可以用字典树,我最开始用的字典树结果爆内存了。。爆内存:#include<iostream>#include<cstdio>#include<cstring>usingnamespacestd;constintmaxn=200000;intn,fa[maxn],trie[maxn][52],cnt,id[2000000],nu......
  • hdu 2795(线段树)
    解题思路:这道题很难想到是用线段树,确实转化的很巧妙实际上,我们只需要利用线段树(记录1-h)维护哪个位置的剩余空间最大即可,即1,2,......,h是线段树的叶子节点,我们每次要找的就是叶子节点的剩余空间的最大值,只要能够想到这里就很容易啦。。另外如果h>n的话,就令h=n,否则就是类似于位置多......
  • hdu 1698(线段树区间更新)
    解题思路:线段树区间更新水题。#include<iostream>#include<cstdio>#include<cstring>usingnamespacestd;constintmaxn=100005;structseg{ intl,r,sum,lazy;}tree[maxn<<2];voidbuild(intl,intr,intu){ tree[u].l=l; tree[u].r=r; t......
  • poj 1324(BFS+状态压缩)
    解题思路:这道题一开始的想法就是状态压缩,即考虑如何判重,由于蛇并非是直线的,所以想到了以每一个点的上下左右共四个值来表示相对位置。最开始想如何用四进制来表示它,无语。。。。。还是题目做少了,直接用两位来表示一个点即可(两位的二进制数可以表示0-3)。剩下的关键就是判断蛇头会不......
  • hdu 1511(dp)
    解题思路:两次dp确实很巧妙,我只想到了一次dp算出最后那个数最小,其实题目要求还要保证第一个数尽可能大,一开始也没有注意到。。AC:#include<stdio.h>#include<string.h>#include<algorithm>#include<iostream>#include<math.h>#include<stdlib.h>#include<time.h>usingnamesp......
  • hdu 5102(队列+节点扩展)
    TheK-thDistanceTimeLimit:8000/4000MS(Java/Others)    MemoryLimit:65536/65536K(Java/Others)ProblemDescriptionGivenatree,whichhasnnodeintotal.Definethedistancebetweentwonodeuandvisthenumberofedgeontheirunique......
  • hdu 5367(线段树+区间合并)
    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5367官方题解:对于求“高山脉”长度,可以利用线段树。树节点中保存左高度连续长度,左高度连续区间的高度,左高度连续区间的状态(即是否高于第一个高度不同的山),右高度连续长度,右高度连续区间的高度,右高度连续区间的状态,每段区间内的“......