题目链接:

UVA : http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=24&page=show_problem&problem=257

POJ  1137: http://poj.org/problem?id=1137

ZOJ  1301: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1301

类型: 隐式图搜索


原题:

Mr. Black recently bought a villa in the countryside. Only one thing bothers him: although there are light switches in most rooms, the lights they control are often in other rooms than the switches themselves. While his estate agent saw this as a feature, Mr. Black has come to believe that the electricians were a bit absent-minded (to put it mildly) when they connected the switches to the outlets.

One night, Mr. Black came home late. While standing in the hallway, he noted that the lights in all other rooms were switched off. Unfortunately, Mr. Black was afraid of the dark, so he never dared to enter a room that had its lights out and would never switch off the lights of the room he was in.

After some thought, Mr. Black was able to use the incorrectly wired light switches to his advantage. He managed to get to his bedroom and to switch off all lights except for the one in the bedroom.

You are to write a program that, given a description of a villa, determines how to get from the hallway to the bedroom if only the hallway light is initially switched on. You may never enter a dark room, and after the last move, all lights except for the one in the bedroom must be switched off. If there are several paths to the bedroom, you have to find the one which uses the smallest number of steps, where ``move from one room to another'', ``switch on a light'' and ``switch off a light'' each count as one step.

Sample Input

3 3 4
1 2
1 3
3 2
1 2
1 3
2 1
3 22 1 2
2 1
1 1
1 20 0 0

Sample Output

Villa #1
The problem can be solved in 6 steps:
- Switch on light in room 2.
- Switch on light in room 3.
- Move to room 2.
- Switch off light in room 1.
- Move to room 3.
- Switch off light in room 2.Villa #2
The problem cannot be solved.

题目大意:

一个2B青年赚钱了于是乐呵呵地跑去买了一栋别墅,结果却发现这栋别墅的电灯电路都接错了,每个房间里的电灯开关控制的不是这个房间的电灯,而是另一个房间的电灯~~ 2B青年很生气,后果很严重,于是严重谴责了装修工人。

一天晚上2B青年回到家里,站在大厅,发现除了大厅其它房间的电灯全部都是灭的。2B青年很怕黑,不敢走进没开灯的房间。所以问题来了,他该怎样从大厅走到自己的卧室?

2B青年还是很聪明的,他发现正好可以利用电灯开关接错而把另一个房间的电灯打开,然后就可以走进开了灯的那个房间(2B青年不会穿墙术 ,所以这两个房间当然必须是相邻的并且有门的)。

2B青年也是个很节约用电的好青年,所以当他走到卧室时,要求其它房间都必须把灭掉。

现在是你大显身手的时候,编写一个程序帮2B青年找出一个步骤最少的方案并且输出。 开灯,关灯,走进另一个房间都算是一个步骤。如果找不出方案的话,2B青年今晚就得睡大厅沙发了。。。

分析与总结:

最近做了不少这类型的题,也形成了自己做搜索题的一个基本思路过程,下面当作是小小的自我总结:

1.  首先是需要找一个“状态”, 这个状态可以看作是一个全局地图, 这题中,状态是表示各个房间的开关灯情况,以及2B青年当前站在哪一个房间里。 0表示关灯,1表示开灯,2表示2B青年站的位置(当然这个位置的灯也必须是亮的)。

2.  然后是找令“状态转移”的“动作”, 这题共有三个动作会导致状态转移:1)开灯;  2)关灯; 3)走进另一个房间。 也就是说做其中任何一个动作都会导致当前状态改变。

3.  进行搜索。在搜索函数中,会进行这几个动作,从而产生新的状态, 然后把新状态压入队列或者栈(bfs或者dfs),之后再对新状态又产生新的状态……一般都会有一个“目标状态”, 当从初始状态一直做动作直到状态转移成目标状态, 那么搜索就完成。

4. 状态重复问题, 也就是判重问题。当产生的新状态在之前已经有过了,那么这个状态就不再拓展新状态。刚开始做bfs和dfs时一般都会有一个vis数组来标记走过的状态, 如果状态比较大,就难以直接开一个数组来做,而需要用到哈希表判重,编码或者用STL中的map和set, 用map和set的效率比较低,经常会导致超时,前两者更常用。

这题我很2B的WA了好几个小时, 一直找不到原因。最后还是觉得自己的逻辑思路没有问题。 于是再找。。。找啊找。。。。

终于给我发现了原因: 一个常识性的错误!

我在保存路径时,路径的输出结果是保存在path字符串数组中,输出结果要输出:

switch …… 房间号,move  to …… 房间号。

问题就出在了房间号上, 由于需要把int的数字转换成char字符串型, 所以我习惯性的这样处理: str[0]=v+'0', 其中v是房间号。那么如果v小于10的话没问题,可以如果v大于等于10,那么结果将是一个XXX不知道是什么的字符。

一个下午的时间就因为这个而消逝了。。。

这个血的教训我将铭记!!

/**  UVa 321  The New Villa*  隐式图搜索, 哈希判重*  Time: 0.024s(UVA), 10ms(ZOJ)*  Author: D_Double**/#include<cstdio>
#include<cstring>
#define MAXN 50000typedef int State[11];
State que[MAXN];
int nRoom, nDoor, nSwitch, G[20][20], control[20][20];
int step[MAXN], father[MAXN], ans;//保存步数,父亲点
char action[3][35] = {"- Move to room ","- Switch on light in room ","- Switch off light in room "};
char path[MAXN][35]; // 保存路径const int HASHSIZE = 2000000;  // 哈希表尺寸
int head[HASHSIZE], next[MAXN];inline int BKDRHash(State &s){ // 哈希函数   int seed = 131; // 31 131 1313 13131 131313 etc..  int hash = 0;  for(int i=1; i<=nRoom; ++i)hash = hash * seed + s[i];return (hash & 0x7FFFFFFF) % HASHSIZE;
}  inline int try_to_insert(int s){int h = BKDRHash(que[s]);int u = head[h];while(u){if(memcmp(que[u], que[s], sizeof(que[s]))==0) return 0;u = next[u];}next[s] = head[h]; head[h] = s;return 1;
}inline bool isOk(State &s){  // 判断是否达到目标状态for(int i=1; i<nRoom; ++i) if(s[i]) return false;if(s[nRoom]==2) return true;return false;
}
// 执行的动作
inline bool run(State &s, int u, int v, int d){switch(d){case 0: // 进入房间 if(G[u][v] && s[v]){s[v] = 2; s[u] = 1; return true;}return false;case 1: // 开灯if(control[u][v] && !s[v]){s[v] = 1; return true;}return false;case 2: // 关灯if(control[u][v] && s[v]){s[v] = 0; return true;}return false;}
}inline void init(){memset(head, 0, sizeof(head));memset(que[0], 0, sizeof(que[0])); que[0][1] = 2; // 1表示灯亮,0不亮,2表示人的位置(灯亮的)step[0] = 0; father[0] = -1;
}void bfs(){init();int front=0, rear=1;while(front < rear){State &s = que[front];if(isOk(s)){ ans = front; return; }int u; // 获取当前在哪个房间for(int i=1; i<=nRoom; ++i) if(s[i]==2){u = i; break;}for(int v=1; v<=nRoom; ++v)if(u!=v){for(int j=0; j<3; ++j){State &t = que[rear];memcpy(t, s, sizeof(s));if(run(t, u, v, j)){if(try_to_insert(rear)){father[rear] = front;step[rear] = step[front]+1;            char str[4];// 保存路径, 注意当房间号大于9时,不能直接用v+'0'if(v<10){  str[0]=v+'0', str[1]='.', str[2]='\0'; }else{ str[0]='1', str[1]='0', str[2]='.', str[3]='\0'; }strcpy(path[rear], action[j]);strcat(path[rear], str);rear++;}}}}        front++;}ans = -1;
}
void print_path(int cur){if(father[cur]!=-1){print_path(father[cur]);printf("%s\n", path[cur]);}
}int main(){int u,v,cas=1;while( scanf("%d%d%d",&nRoom,&nDoor,&nSwitch), nRoom){memset(G, 0, sizeof(G));memset(control, 0, sizeof(control));for(int i=0; i<nDoor; ++i){ scanf("%d %d",&u, &v); G[u][v]=G[v][u]=1; }for(int i=0; i<nSwitch; ++i) { scanf("%d %d", &u, &v); control[u][v]=1; }bfs();printf("Villa #%d\n", cas++);if(ans != -1){printf("The problem can be solved in %d steps:\n", step[ans]);print_path(ans);}elseprintf("The problem cannot be solved.\n");printf("\n");}return 0;
}

但是故事还未结束, 当我把程序交到UVA,ZOJ 后正在庆祝AC时, 交到POJ的评测结果再次把沉重地打击了我:WA

然后看了poj的discuss, 发现原来POJ的测试数据都是优先move进入一个房间,然后再开关灯的。这样的话,需要在搜索时,优先拓展move的状态, 稍微改了一下之后在poj成功AC:

/**  POJ 1137  The New Villa*  隐式图搜索, 哈希判重*  Time: 94MS(POJ)*  Author: D_Double**/#include<cstdio>
#include<cstring>
#define MAXN 50000typedef int State[11];
State que[MAXN];
int nRoom, nDoor, nSwitch, G[20][20], control[20][20];
int step[MAXN], father[MAXN], ans;//保存步数,父亲点
char action[3][35] = {"- Move to room ","- Switch on light in room ","- Switch off light in room "};
char path[MAXN][35]; // 保存路径const int HASHSIZE = 2000000;  // 哈希表尺寸
int head[HASHSIZE], next[MAXN];inline int BKDRHash(State &s){ // 哈希函数   int seed = 131; // 31 131 1313 13131 131313 etc..  int hash = 0;  for(int i=1; i<=nRoom; ++i)hash = hash * seed + s[i];return (hash & 0x7FFFFFFF) % HASHSIZE;
}  inline int try_to_insert(int s){int h = BKDRHash(que[s]);int u = head[h];while(u){if(memcmp(que[u], que[s], sizeof(que[s]))==0) return 0;u = next[u];}next[s] = head[h]; head[h] = s;return 1;
}inline bool isOk(State &s){  // 判断是否达到目标状态for(int i=1; i<nRoom; ++i) if(s[i]) return false;if(s[nRoom]==2) return true;return false;
}
// 执行的动作
inline bool run(State &s, int u, int v, int d){switch(d){case 0: // 进入房间 if(G[u][v] && s[v]){s[v] = 2; s[u] = 1; return true;}return false;case 1: // 开灯if(control[u][v] && !s[v]){s[v] = 1; return true;}return false;case 2: // 关灯if(control[u][v] && s[v]){s[v] = 0; return true;}return false;}
}inline void init(){memset(head, 0, sizeof(head));memset(que[0], 0, sizeof(que[0])); que[0][1] = 2; // 1表示灯亮,0不亮,2人的位置(灯亮的)step[0] = 0; father[0] = -1;
}void bfs(){init();int front=0, rear=1;while(front < rear){State &s = que[front];if(isOk(s)){ ans = front; return; }int u; // 获取当前在哪个房间for(int i=1; i<=nRoom; ++i) if(s[i]==2){u = i; break;}for(int v=1; v<=nRoom; ++v)if(u!=v){  // 先进行move操作State &t = que[rear];memcpy(t, s, sizeof(s));if(run(t, u, v, 0)){if(try_to_insert(rear)){father[rear] = front;step[rear] = step[front]+1;char str[4];  // 保存路径, 注意当房间号大于9时,不能直接用v+'0'  if(v<10){  str[0]=v+'0', str[1]='.', str[2]='\0'; }  else{ str[0]='1', str[1]='0', str[2]='.', str[3]='\0'; }  strcpy(path[rear], action[0]);  strcat(path[rear], str);  rear++;}}}for(int v=1; v<=nRoom; ++v)if(u!=v){ // 然后再开关灯State &t = que[rear];memcpy(t, s, sizeof(s));if(run(t, u, v, 1)){  // 开灯if(try_to_insert(rear)){father[rear] = front;step[rear] = step[front]+1;// 保存路径, 注意当房间号大于9时,不能直接用v+'0'char str[4];if(v<10){  str[0]=v+'0', str[1]='.', str[2]='\0'; }  else{ str[0]='1', str[1]='0', str[2]='.', str[3]='\0'; }  strcpy(path[rear], action[1]);  strcat(path[rear], str);  rear++;}}else if(run(t, u, v, 2)){  // 关灯if(try_to_insert(rear)){father[rear] = front;step[rear] = step[front]+1;// 保存路径, 注意当房间号大于9时,不能直接用v+'0'char str[4];if(v<10){  str[0]=v+'0', str[1]='.', str[2]='\0'; }  else{ str[0]='1', str[1]='0', str[2]='.', str[3]='\0'; }  strcpy(path[rear], action[2]);  strcat(path[rear], str);  rear++;}  }}       front++;}ans = -1;
}
void print_path(int cur){if(father[cur]!=-1){print_path(father[cur]);printf("%s\n", path[cur]);}
}int main(){int u,v,cas=1;while( scanf("%d%d%d",&nRoom,&nDoor,&nSwitch), nRoom){memset(G, 0, sizeof(G));memset(control, 0, sizeof(control));for(int i=0; i<nDoor; ++i){ scanf("%d %d",&u, &v); G[u][v]=G[v][u]=1; }for(int i=0; i<nSwitch; ++i) { scanf("%d %d", &u, &v); control[u][v]=1; }bfs();printf("Villa #%d\n", cas++);if(ans != -1){printf("The problem can be solved in %d steps:\n", step[ans]);print_path(ans);}elseprintf("The problem cannot be solved.\n");printf("\n");}return 0;
}
——  生命的意义,在于赋予它意义。 
          
     原创 http://blog.csdn.net/shuangde800 , By   D_Double  (转载请标明)

UVa 321 The New Villa,2B青年怒找卧室相关推荐

  1. UVA 321 The New Villa

    UVA_321 这个题目可以把所有灯的开关状态以及当前人所在的屋子看成一个状态,这样的话最多只有n*2^n个状态,由于n很小,还是可以接受的,直接进行隐式图搜索即可. 在用Hash判重时,为了方便我们 ...

  2. 2B青年答疑:什么是2B青年?如何脱离2B青年行列?

    什么叫2B青年?一个很模糊的概念. 首先,这个明显是北方骂人的话,意义基本等同傻逼.缺心眼,思维方式和常人有别还有智商低下是其基本特征. 然后,现在意义被引申了,在这个和谐社会,傻逼算是这个社会上唯一 ...

  3. 普通青年、文艺青年、2B青年---最近老火了

    普通青年->文艺青年->2B青年  进化图 常态波波--文艺波波--二逼波波 常态邓呆--文艺邓呆--二逼邓呆 话题区的科比不甘示弱: 普通科比.文艺科比.2B科比-- 写诗的麦迪拿起了照 ...

  4. 2b青年快乐多啊,转的关于程序猿的笑话

    1.栈和队列的区别是啥? 吃多了拉就是队列:吃多了吐就是栈 2.世界上最遥远的距离不是生与死,而是你亲手制造的BUG就在你眼前,你却怎么都找不到她... 3.<c++程序设计语言>比< ...

  5. 我的2012,一个2b青年的表白

    我是一个2b青年,走了一条2b的路子 大学的我,跟大部分人一样,专业课学的不太专业,开始了一场自始至终都很痛苦的爱情.考了两次研究生,也签了一家外企公司,在一家初创公司工作过,在一家非it公司工作.最 ...

  6. 从文艺青年到2B青年,轻博客的N种玩法

    "轻博客"是2011年最火的中国互联网发展趋势之一,因为"点点网"的捷足先登,很多人都认为"轻博客"就是"点点",我也如 ...

  7. if语句的写法之普通,文艺,2B青年写法

    最近这段时间在处理一个项目迁移,结果有大量的时间在写迁移代码. 发现软件确实是门工艺,是需要有充足的时间和精力去做些一些重复的事情,这样才能有技能上的提高. 下面就用一个很普通常见的参数判断的例子来说 ...

  8. 文艺青年、普通青年、2b青年到底是什么意思?

    文艺青年.普通青年.2b青年到底是什么意思? 文艺青年就是脑子里跟别人想的不一样,思维跟人家相反或者另类的人. 普通青年呢就是像你一样,普普通通的. 2B青年就是黑铅笔青年,做事比较搞怪,古怪到让你哭 ...

  9. 普通青年 文艺青年 2B青年

    本文纯属搞笑,如有雷同,纯属巧合. /* normal-youth.c */main() { printf("hello world");} /* literature-youth ...

最新文章

  1. I hope so 2016-Oct-10
  2. 使用OPENROWSET爆破SQL Server密码
  3. 关于数据事实表汇总的模拟实现——原理
  4. 4. time datetime 时间模块
  5. 61.新的开始(最小生成树)
  6. leetcode 寻找两个有序数组的中位数
  7. 做好这5点基本要求 才能算一个合格的HTML5动画
  8. pandas 在某个列表中的值 筛选列_Pandas学习笔记(二)
  9. 读写锁分离的循环队列
  10. php可输入的下拉框,JavaScript_可编辑下拉框的2种实现方式,可编辑下拉框-HTML 复制代码 代 - phpStudy...
  11. [转]linux 调用动态库so文件
  12. Java编程ture找不到符号,[未解决]Bugly中上传符号表dSYM文件
  13. 百度地图和谷歌地图经纬度互转
  14. [视频教程]macOS运行MAME
  15. 使用CDN加速的优点
  16. 爬虫:爬取豆果网和美食网的菜单
  17. A Survey on Conversational Recommender Systems
  18. oracle中private同义词和public同义词
  19. 图像分析之直方图分析
  20. 深度清理win10系统C盘文件(二)

热门文章

  1. Basic -- Test harness
  2. CK默认存储目录迁移
  3. Linux下 C 遍历目录(opendir,readdir函数)
  4. MeiShe Face Sticker Design Plugin AR Scene Editor Instruction
  5. flam3 ubuntu 依赖文件
  6. 计算机应用广告设计论文,广告设计计算机论文,关于中文字体设计在计算机广告中的运用相关参考文献资料-免费论文范文...
  7. 商业智能在医疗卫生领域的应用与前景
  8. 20170305Meetup Git、heroku drop db
  9. Java实现 LeetCode 526 优美的排列(DFS)
  10. 号称下一代监控系统,到底有多牛逼!