UVa 321 The New Villa,2B青年怒找卧室

2023-12-10 04:58
文章标签 new 卧室 uva 青年 2b 321 villa

本文主要是介绍UVa 321 The New Villa,2B青年怒找卧室,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目链接:

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青年怒找卧室的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/476070

相关文章

UVA - 12206 Stammering Aliens (hash)

这题其实很容易想到2分长度,关键是2分后,怎么判断出现最多次的串是否》=m次了。这里需要用到hash来处理。 对于一个字符串   H[i] =H[i+1]*131+s[i]  ;其中 H[n]=0;那么对于一个长度为L的串 ,hanh[i,l]=H[i]-H[i+L]*x^L ; 这样记录每个字符串的hash值,然后再处理起来就比较简单了。 VIEW CODE #include<

github 报错 git fatal: unable to write new index file

错误一:git fatal: unable to write new index file主要原因就是服务器磁盘空间不够导致的,增加服务器空间就OK了在百度上面搜索没得到什么有效信息,在gooogle上搜索得到很多有效信息 Finding large directories with something like the following helped clean up some log fi

Failed to establish a new connection: [WinError 10061] 由于目标计算机积极拒绝,无法连接

在进行参数化读取时发现一个问题: 发现问题: requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=8081): Max retries exceeded with url: /jwshoplogin/user/update_information.do (Caused by NewConn

c.toString() 和 String s = new String(c) 区别

String str = "abcd";char [] c = str.toCharArray();String s = new String(c);String s2 = c.toString();其中s和s2有什么区别???String str = "abcd";char [] c = str.toCharArray();String s = new String(c); //

获取时间戳是使用System.currentTimeMillis()还是使用new Date().getTime()(阿里开发规范)?

1.阿里规范 在阿里的Java开发手册中强制要求使用System.currentTimeMillis() 2.为什么(源码详解) new Date().getTime()它实际上也是调用的System.currentTimeMillis(),源码分析。 这个fastTime是它的成员变量,在new Date()的时候就被赋值了。 扩展一下这个transient这个关键字,它是为了保护

不需要new关键字创建实例?jQuery是如何做到的

这篇文章是jQuery源码专栏的开篇文章了,有人会问为什么都2024年了, 还要研究一个已经过时的框架呢,其实,jQuery对比vue和react这种响应式框架,其在使用上算是过时的,毕竟直接操作DOM远不如操作虚拟DOM来的方便,但是jQuery的框架设计和对于操作的封装以及浏览器的兼容这些,太值得我们去学习了。   这个专栏更新的速度不会快,这框架代码我是刚开始进行了解,所以只能边看边查

Xamarin.ios 解决new NSUrl 返回为空的方法。

var uri = new Uri (urlString);var nsurl = new NSUrl (uri.GetComponents (UriComponents.HttpRequestUrl, UriFormat.UriEscaped));UIApplication.SharedApplication.OpenUrl (nsurl);

湖南科技大学24计算机考研情况,软工学硕考数二,分数线290分,录取均分321分!

湖南科技大学(Hunan University of Science and Technology)坐落在伟人故里、人文圣地湘潭,处于长株潭核心区域,比邻湘潭九华经济技术开发区(国家级),是应急管理部、国家国防科技工业局与湖南省人民政府共建高校、“十三五”国家百所中西部高校基础能力建设工程支持高校、国家大学生文化素质教育基地、教育部本科教学工作水平评估“优秀”高校、教育部“卓越工程师教育培养计划”

SP2010开发和VS2010专家食谱--第六章节--Web Services和REST(5)--Inserting new contacts through REST

我们现在知道了我们可以使用REST请求从SharePoint列表获得数据,如何从客户端应用程序添加数据到列表呢?本文中,我们将探讨如何做到。

[Uva 11990] Dynamic Inversion (CDQ分治入门)

Uva - 11990 动态逆序对,求删除一个点之前序列中逆序对的个数 首先倒过来看这个问题,把点先全部删掉 然后从最后一个点开始倒着往数列中加点 求加完这个点逆序对的变化 CDQ分治做法 把删除时间看作 t,下标看作 x,值看作 y 然后对 x排序,对 t偏序,用树状数组维护 y 具体来说就是对于每个点 (t0,x0,y0) (t_0, x_0, y_0) 先统计