HDU4012 Paint on a Wall(BFS)

一开始确实没看出来什么玄机。然后搜了搜题解。由于最多只有16个格子,用二进制的状态压缩来表示,状态为1的位表示该位已经涂上了正确的颜色。
从第一行的i号节点开始向两边涂色的时候注意:如果刷到的这个节点已经是它本身的正确的颜色,那么我们就不刷了。否则这样又刷回了错误的颜色,就不是最优解了。然后把每扩展一个格子的状态全部压入队列,也就是代码中的tmp。
此外,同时刷两行颜色的时候只用考虑第一行就行了。

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
struct Node
{
    int s,stp;
    Node(){}
    Node(int a,int b)
    {s = a; stp = b;}
}st;
char str[20];
bool vis[1<<16];
int n,cas;
void bfs()
{
    int ed = (1<<(2*n))-1;
    memset(vis,0,sizeof vis);
    queue<Node> Q;
    Q.push(st);
    while(!Q.empty())
    {
        Node u = Q.front(),v;
        Q.pop();
        if(u.s == ed)
        {
            printf("Case #%d: %d\n",cas,u.stp);
            return;
        }
        for(int i = 0; i < 2*n; i++)
        {
            if((1<<i)&u.s) continue;
            int tmp = 0;
            for(int j = i; j < (i/n+1)*n; j++)//右刷,这种写法把两行的情况都考虑了,很巧妙
            {
                if((1<<j)&u.s) break;
                if(str[i] == str[j]) tmp |= (1<<j);
            }
            for(int j = i-1; j >= (i/n)*n; j--)//左刷
            {
                if((1<<j)&u.s) break;
                if(str[i] == str[j]) tmp |= (1<<j);
            }
            for(int j = tmp; j; j = (j-1)&tmp)//神奇的判断子集的方法,可以动手实验一下
            {
                if(vis[u.s|j]) continue;
                vis[u.s|j] = 1;
                Q.push(Node(u.s|j,u.stp+1));
            }
            if(i >= n) break;
            if((1<<(i+n))&u.s) continue;
            tmp = 0;
            for(int j = i; j < n; j++)
            {
                if(((1<<j)&u.s)||((1<<(j+n))&u.s)) break;
                if(str[i] == str[j]) tmp |= 1<<j;
                if(str[i] == str[j+n]) tmp |= 1<<(j+n);
            }
            for(int j = i-1; j >= 0; j--)
            {
                if(((1<<j)&u.s)||((1<<(j+n))&u.s)) break;
                if(str[i] == str[j]) tmp |= 1<<j;
                if(str[i] == str[j+n]) tmp |= 1<<(j+n);
            }
            for(int j = tmp; j; j = (j-1)&tmp)
            {
                if(vis[u.s|j]) continue;
                vis[u.s|j] = 1;
                Q.push(Node(u.s|j,u.stp+1));
            }
        }
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        cas++;
        scanf("%d",&n);
        scanf("%s",str);
        scanf("%s",str+n);
        st.s = st.stp = 0;
        bfs();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值