大夫值班问题
医院有A、B、C、D、E、F、G七位大夫,在一个星期内(星期一至星期天)
每天要轮流值班一天。现在已知:
A大夫比C大夫晚一天值班;
D大夫比E大夫晚两天值班;
B大夫比G大夫早三天值班;
F大夫的值班日在B和C大夫的中间,且是星期四;
请编程确定每天究竟哪位大夫值班?
**输出格式要求:"Doctor %c is on duty %s.\n"
程序运行示例如下:
Doctor E is on duty MONDAY.
Doctor B is on duty TUESDAY.
Doctor D is on duty WEDNESDAY.
Doctor F is on duty THURSDAY.
Doctor G is on duty FRIDAY.
Doctor C is on duty SATURDAY.
Doctor A is on duty SUNDAY.
代码
书接上回。
某个风和日丽的下午笔者正在快乐地敲着oj,突然被一道杀软题目乱了阵脚,这道赤裸裸的逻辑题被放在c语言OJ里就是出题人纯纯的恶意。
看了下网上不论是csdn还是stack overflow,都表示这是一道算法题并且最离谱的是表示“直接套七层穷尽循环就可以了”。这如同屎山一般的代码让人实在无法忍受,于是笔者愤而手搓七天七十多行代码,终于在一个晚上把这玩意儿给跑出来了。
或许我的答案不是标准答案,但是亲手解出一个难题的感觉永远无法忘怀。
老规矩,先放代码后讲解。
#include "stdio.h"
int main()
{
int a,b,c,d,e,f,g;
f = 3;
char doctor[7];
char weekday[][10] = {"MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY","SATURDAY","SUNDAY"};
for (c = 0;c < 6;c ++)
{
a = c + 1;
if (a < 3)
{
for (b = 4;b < 7;b ++)
{
g = b + 3;
if (g > 6) g -= 7;
for (e = 0;e < 7;e ++)
{
d = e + 2;
if (d == 3 || e == 3) continue;
if (d > 6) d = e + 2 - 7;
if (a + b + c + d + e + f + g == 21 && b != d && e != d && e != b)
{
doctor[a] = 'A';
doctor[b] = 'B';
doctor[c] = 'C';
doctor[d] = 'D';
doctor[e] = 'E';
doctor[f] = 'F';
doctor[g] = 'G';
for (int i = 0;i < 7;i ++)
{
printf("Doctor %c is on duty %s.\n",doctor[i],weekday[i]);
}
}
}
}
}
if (c > 3)
{
a = c + 1;
if (a == 7) a = 0;
for (b = 0;b < 3;b ++)
{
g = b + 3;
if (g == 3) continue;
for (e = 0;e < 6;e ++)
{
d = e + 2;
if (d == 3 || e == 3) continue;
if (d > 6) d = e + 2 - 7;
if (a + b + c + d + e + f + g == 21 && b != d && e != d && e != b)
{
doctor[a] = 'A';
doctor[b] = 'B';
doctor[c] = 'C';
doctor[d] = 'D';
doctor[e] = 'E';
doctor[f] = 'F';
doctor[g] = 'G';
for (int i = 0;i < 7;i ++)
{
printf("Doctor %c is on duty %s.\n",doctor[i],weekday[i]);
}
}
}
}
}
}
return 0;
}
详细讲解
要解决这个问题,可以使用逻辑推理和编程相结合的方式。首先,根据已知条件列出一些关系,然后使用这些关系来确定每位大夫在一周内的值班日。
已知条件:
- F大夫的值班日是星期四。
- F大夫的值班日在B和C大夫的中间。
- B大夫比G大夫早三天值班。
- A大夫比C大夫晚一天值班。
- D大夫比E大夫晚两天值班。
首先从F大夫的值班日开始推理:
- F大夫的值班日是星期四,所以 F = 星期四。
- F大夫的值班日在B和C大夫的中间,所以B大夫的值班日必须在F大夫之前,C大夫的值班日必须在F大夫之后。
- B大夫比G大夫早三天值班,因此G大夫的值班日必须在B大夫之后三天。
现在我们可以开始尝试填充一周的日期:
- 星期四:F大夫
- 星期三:不能是G大夫(因为G大夫在B大夫之后三天),也不能是C大夫(因为F在B和C之间),所以星期三可能是B大夫或D大夫或E大夫。
- 星期二:不能是F大夫或C大夫,可能是B大夫(如果星期三不是B大夫的话)或D大夫或E大夫或G大夫。
- 星期一:不能是F大夫、B大夫或C大夫,可能是D大夫或E大夫或G大夫。
- 星期五:不能是F大夫,必须是C大夫(因为F在B和C之间)。
- 星期六:不能是F大夫或C大夫,必须是A大夫(因为A比C晚一天)。
- 星期日:剩下的就是G大夫。
此列需要很强的逻辑推理,各位可以自行想一下。接下来,我们采用遍历的方式,将我们的思考过程复现。
经过慎重考虑,c应为遍历的那一个。因为在该逻辑中,a与c始终深度绑定,在设想的可能性中,让c带着a遍历,而b又与c隔着f对立,因此代码遍历分为两种情况:a < 3和a > 3.
先把各个医生的值班关系导出。
a = c + 1;
g = b + 3;
d = e + 2;
//在计算的过程中,a、g、d随时有可能超出0-6的范围,要考虑其超出的的情况,从零重新开始计数。
随后以思维导图的方式画一张伪代码,导图如下。
1. 这里的a + b + c + d + e + f + g == 21是什么意思?
在对所有的可能性做遍历后,想要快速识别出其正确排序过于冗长(毕竟要使所有的变量都不想等)。因此可以先将正确的天数之和 = 21做判断,排除掉一部分选项。
2.b != d && e != d && e != b呢?
然后根据前面的分析,星期三可能是B大夫或D大夫或E大夫,但不可能同时值班。因此排除选项重复的可能性,至此基本排除了可能的重复选项。
3.如何输出?
这么简单的问题笔者甚至想了很长时间...
由答案看结果,星期是按照顺序输出的,直接在遍历中将weekday输出即可。已经确定了天数的a,b,c,d,e,f,g小可爱们,在开头重新定义一个数组doctor,将排好的天数作为数组下标,逐一分配给医生A-G即可。
over。
战斗!爽!(