POJ - 1137:The New Villa(bfs+状态压缩)

本文详细解析了一道经典的BFS算法题目,通过实例讲解如何定义状态、实现状态转移及存储路径,适合初学者掌握BFS的基本应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


题目链接:点击打开链接


题目大意:

题目比较长,就是说有一个青年,他买了一栋大别墅,别墅里面有很多的房子,房子之间有些相互连通,但是这些房子有个毛病,就是说一个房间灯的开关可能在另外一个房间。现在青年在第一个房间,他想到最后一个房间,但是因为他怕黑,所以他只能进亮着灯的房间,同理,自己在这个房间的话,也不能关闭这个房间的灯。问最少需要多少步才能到最后一个房间且除最后一个房间外,将其他所有房间的灯都关掉。


解题思路:


刚开始完全是蒙蔽的,后来仔细的想了一下也并没有太复杂。但是感觉这道题比较经典,最近做的bfs又比较多,就顺带借这道题把自己解bfs的思路说一下,


      首先确定状态,bfs里面比较重要的就是状态的转移,从一个状态转移到另外一个状态。同时也要保证同样的状态不会重复第二次。例如这道题的状态就是当前所在房间和整体所有房间的灯的开关情况,那么就可以先确定下来用个二维数组存状态,一部分储存当前房间号一部分储存所有灯的情况。灯的情况因为不超过10个房间,那么就直接二进制表示即可。

     确定状态表示之后就要想状态怎么转移,这道题比较简单,利用位运算的各种功能就可以实现某个房间的灯的开关。

     最后这道题要你实现路径,那么就可以搬出来我们的路径杀手string了,直接一路储存下来,是移动开灯还是关灯打个标记,直接输出即可。


接下来就是一些做题要注意的地方

刚开始坐这道题的时候还以为是一个房间只有一个开关,一个开关控制这个房间能控制的所有灯,然后每次按了这个开关后跟这个开关有关的灯的情况会反转,我的妈呀,幸好没这么想。其实就是每个房间很多开关,你依次按就好,一个灯一个灯的来,

因为POJ是没有特判的,所以POJ做的时候要注意一个地方,就是状态转移的时候,是有优先级的 具体是 move>off>on 。按照这个顺序来就没有问题,同时注意关灯的顺序是要按照最小字典序来的,其实就是从小到大排个序,用vector直接排序就行了。

其次还有一个比较奇怪的地方是我数组和vector大小如果开成15(理论上15是对的,因为整个程序是按照房间数最大为10写的),在Uva上RE了,POJ却A了,之后测试发现如果后面的d,s比较大我的cb会显示程序崩溃,但是队友的VS显示的答案跟预期相符。费解,反正扩大到150之后Uva就A了。。。初步预测可能是c++和g++编译器的问题。


以下贴代码,为在Uva和POJAC的代码,其他oj如果wa或者re概不负责,


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
#include <algorithm>
#include <stack>
#include <set>
#include <functional>
#define rank ra
#define next ne
#define pb push_back
#define hash haha
#define lson rt<<1
#define rson rt<<1|1
#define xlson xl,xmid,xt<<1
#define xrson xmid+1,xr,xt<<1|1
#define ylson yl,ymid,xt,yt<<1
#define yrson ymid+1,yr,xt,yt<<1|1
using namespace std;
typedef long long ll;
const int N=1<<11;
int r,d,s,flag,scp;
string ans;         //最终的路径
vector <int> dv[150];   //房间之间的关系
vector <int> lv[150];   //灯的关系
bool vis[150][N+10];    //状态数组
int dx[4]={0,-1,0,1};
int dy[4]={-1,0,1,0};
struct node
{
    int s;
    int sta;
    int step;
    string dir;
};
char vs(int k)  //别管~看bfs
{
    char cc;
    cc=k+'a';   //这里随便+一个正常的符号记得输出的时候减回来
    return cc;  //为啥要加这个,不知道,就想这么写。
}
void bfs()
{
    memset(vis,0,sizeof(vis));
    queue<node> que;
    node st;
    st.sta=1;   //初始状态只有第一个房间开着灯
    st.s=1;
    st.step=0;
    que.push(st);
    flag=0;
    node k1;
    string tmp;
    while(!que.empty())
    {
        node k=que.front();
        que.pop();
        if(k.s==r&&k.sta==(1<<(r-1)))   //判断是否到终点且只有最后一个房间开着灯
        {
            printf("The problem can be solved in %d steps:\n",k.step);
            flag=1;
            ans=k.dir;
            break;
        }
        for(int i=0;i<(int)dv[k.s].size();i++)   //首先移动到其他开着灯的房间
        {
            int xx=dv[k.s][i];
            if(vis[xx][k.sta]==0&&((1<<(xx-1))&k.sta))
            {
                k1.s=xx;
                k1.sta=k.sta;
                k1.step=k.step+1;
                tmp=k.dir+"M";
                k1.dir=tmp+vs(k1.s);
                que.push(k1);
                vis[k1.s][k1.sta]=1;
            }
        }
        for(int i=0;i<(int)lv[k.s].size();i++)  //其次依次关闭灯 注意不可关闭当前房间的灯
        {
            int sa=scp^(1<<(lv[k.s][i]-1));     //位运算没想到什么好的实现办法所以写的比较复杂
            k1.s=k.s;                           //可自行手推一遍应该就能明白
            k1.sta=k.sta&sa;
            if(vis[k1.s][k1.sta]==0&&lv[k.s][i]!=k.s)
            {
                k1.step=k.step+1;
                tmp=k.dir+"F";
                k1.dir=tmp+vs(lv[k.s][i]);
                que.push(k1);
                vis[k1.s][k1.sta]=1;
            }
        }
        for(int i=0;i<(int)lv[k.s].size();i++)  //依次开灯 这里位运算过程较为简单
        {
            k1.s=k.s;
            k1.sta=k.sta|(1<<(lv[k.s][i]-1));
            if(vis[k1.s][k1.sta]==0)
            {
                k1.step=k.step+1;
                tmp=k.dir+"N";
                k1.dir=tmp+vs(lv[k.s][i]);
                que.push(k1);
                vis[k1.s][k1.sta]=1;
            }
        }
    }
}
void print_ans()
{
    for(int i=0;i<(int)ans.length();i=i+2)  //根据储存的标记打印答案
    {
        if(ans[i]=='M')
            printf("- Move to room %d.\n",ans[i+1]-'a');
        if(ans[i]=='N')
            printf("- Switch on light in room %d.\n",ans[i+1]-'a');
        if(ans[i]=='F')
            printf("- Switch off light in room %d.\n",ans[i+1]-'a');
    }
}
void init()     //初始化vector
{
    scp=(1<<10)-1;
    for(int i=0;i<=10;i++)
        dv[i].clear();
    for(int i=0;i<=10;i++)
        lv[i].clear();
}
int main()
{
    int kase=0;
    while(scanf("%d%d%d",&r,&d,&s)!=EOF)
    {
        init();
        if(r==0&&d==0&&s==0)
            break;
        int p,q;
        for(int i=1;i<=d;i++)   //建立房间的关系
        {
            scanf("%d%d",&p,&q);
            dv[p].pb(q);
            dv[q].pb(p);
        }
        for(int i=1;i<=s;i++)   //建立灯之间的关系
        {
            scanf("%d%d",&p,&q);
            lv[p].pb(q);
        }
        for(int i=1;i<=s;i++)       //排序
            sort(lv[i].begin(),lv[i].end());
        for(int i=1;i<=d;i++)
            sort(dv[i].begin(),dv[i].end());
        printf("Villa #%d\n",++kase);
        bfs();
        if(flag)
            print_ans();
        else
            printf("The problem cannot be solved.\n");
        printf("\n");
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值