- 题目
【编程题】
某保密单位机要人员 A,B,C,D,E 每周需要工作5天,休息两天。
上级要求每个人每周的工作日和休息日必须是固定的,不能在周间变更。
此外,由于工作需要,还有如下要求:
1. 所有人的连续工作日不能多于3天(注意:周日连到下周一也是连续)。
2. 一周中,至少有3天所有人都是上班的。
3. 任何一天,必须保证 A B C D 中至少有2人上班。
4. B D E 在周日那天必须休息。
5. A E 周三必须上班。
6. A C 一周中必须至少有4天能见面(即同时上班)。
你的任务是:编写程序,列出ABCDE所有可能的一周排班情况。工作日记为1,休息日记为0
A B C D E 每人占用1行记录,从星期一开始。
【输入、输出格式要求】
程序没有输入,要求输出所有可能的方案。
每个方案是7x5的矩阵。只有1和0组成。
矩阵中的列表示星期几,从星期一开始。
矩阵的行分别表示A,B,C,D,E的作息时间表。
多个矩阵间用空行分隔开。
例如,如下的矩阵就是一个合格的解。请编程输出所有解。
0110111
1101110
0110111
1101110
1110110
- 分析
说实在的,拿到这个题,首先得思路就是穷举,然后一个个判断是否合格,但是转念一想(2^35),那得多慢啊。
于是我的进一步的优化是,根据每人每周必须工作五天,休息两天,且连续工作不超过三天,首先分析可能性,我知道这肯定是很小的。
static List<int[]> lst = new ArrayList<int[]>();
static void array() {
//结合条件【每周需要工作5天,休息两天】
for(int i=0;i<7;i++)
for(int j=i+1;j<7;j++) {
int[] arr = {1,1,1,1,1,1,1};
arr[i] = arr[j] = 0;
//验证条件【4】和【5】&&(arr[2]==0||arr[6]==1)
if(check(arr)) {
lst.add(arr);
}
}
}
//验证条件【1】
static boolean check(int[] a) {
int[][] d = {
{0,1,2,3},
{1,2,3,4},
{2,3,4,5},
{3,4,5,6},
{4,5,6,0},
{5,6,0,1},
{6,0,1,2}};
for(int[] t: d)
if(a[t[0]]==a[t[1]]&&a[t[1]]==a[t[2]]&&a[t[2]]==a[t[3]])
return false;
return true;
}
[1, 0, 0, 1, 0, 0, 0]
[1, 0, 0, 0, 1, 0, 0]
[0, 1, 0, 0, 1, 0, 0]
[0, 1, 0, 0, 0, 1, 0]
[0, 0, 1, 0, 1, 0, 0]
[0, 0, 1, 0, 0, 1, 0]
[0, 0, 1, 0, 0, 0, 1]
[0, 0, 0, 1, 1, 0, 0]
[0, 0, 0, 1, 0, 1, 0]
[0, 0, 0, 1, 0, 0, 1]
输出只有八种情况。于是问题大小就变成(8^5=2^15),可以说
小很多了。于是下面就是最终的解决方法。
- 方法一
下面这张图也是结合题目分析出来的。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Calendar {
static List<int[]> lst = new ArrayList<int[]>();
public static void main(String[] args) {
//初步筛选
array();
//进一步筛选
for(int i=0;i<lst.size();i++) {
int[][] test = new int[5][7];
if(lst.get(i)[6]==1)
for(int j=0;j<lst.size();j++)
if(lst.get(j)[6]==0)
for(int m=0;m<lst.size();m++)
if(lst.get(m)[6]==1)
for(int n=0;n<lst.size();n++)
if(lst.get(n)[6]==0)
for(int p=0;p<lst.size();p++)
if(lst.get(p)[2]==1&&lst.get(p)[6]==0) {
test[0] = lst.get(i);
test[1] = lst.get(j);
test[2] = lst.get(m);
test[3] = lst.get(n);
test[4] = lst.get(p);
if(checkagain(test)) {
for(int[] t: test) {
for(int t1: t)
System.out.print(t1);
System.out.println();
}
System.out.println();
}
}
}
}
static void array() {
//结合条件【每周需要工作5天,休息两天】
for(int i=0;i<7;i++)
for(int j=i+1;j<7;j++) {
int[] arr = {1,1,1,1,1,1,1};
arr[i] = arr[j] = 0;
//验证条件【4】和【5】
if(check(arr)) {
lst.add(arr);
//System.out.println(Arrays.toString(arr));
}
}
}
//验证条件【1】
static boolean check(int[] a) {
int[][] d = {
{0,1,2,3},
{1,2,3,4},
{2,3,4,5},
{3,4,5,6},
{4,5,6,0},
{5,6,0,1},
{6,0,1,2}};
for(int[] t: d)
if(a[t[0]]==a[t[1]]&&a[t[1]]==a[t[2]]&&a[t[2]]==a[t[3]])
return false;
return true;
}
//验证条件其他
static boolean checkagain(int[][] a) {
//一周中,至少有3天所有人都是上班的
int cnt1 = 0;
for(int i=0;i<7;i++)
if(a[0][i]+a[1][i]+a[2][i]+a[3][i]+a[4][i]==5)
cnt1++;
if(cnt1<3)
return false;
//任何一天,必须保证 A B C D 中至少有2人上班
for(int i=0;i<7;i++)
if(a[0][i]+a[1][i]+a[2][i]+a[3][i]<2)
return false;
//B D E 在周日那天必须休息
if(a[1][6]==1||a[3][6]==1||a[4][6]==1)
return false;
//A E 周三必须上班
if(a[0][2]==0||a[4][2]==0)
return false;
//A C 一周中必须至少有4天能见面(即同时上班)
int cnt = 0;
for(int i=0;i<7;i++)
if(a[0][i]==a[2][i]&&a[2][i]==1)
cnt++;
if(cnt<4)
return false;
return true;
}
}
- 方法一输出
0110111
1101110
0110111
1101110
1110110
0111011
1110110
0111011
1110110
1110110
1011011
1110110
1011011
1110110
1110110
1011101
1110110
1011101
1110110
1110110
反思
看起来很繁琐,对不对。方法二
方法二思想也是和方法一一样,不过用的是迭代,很简洁,我没有想到还可以这样写
public class Calendar2 {
static final int[][] d = {
{0,1,2,3},
{1,2,3,4},
{2,3,4,5},
{3,4,5,6},
{4,5,6,0},
{5,6,0,1},
{6,0,1,2}};
static int[][] arr = new int[5][7];
public static void main(String[] args) {
array(0);
}
static void array(int row) {
if(row==5) {
if(checkagain(arr))
{
for(int[] t: arr) {
for(int t1: t)
System.out.print(t1);
System.out.println();
}
System.out.println();
}
return ;
}
//结合条件【每周需要工作5天,休息两天】
for(int i=0;i<7;i++)
for(int j=i+1;j<7;j++) {
for(int k=0;k<7;k++)
arr[row][k] = 1;
arr[row][i] = arr[row][j] = 0;
//验证条件【4】和【5】
if(check(arr[row])) {
array(row+1);
}
}
}
//验证条件【1】
static boolean check(int[] a) {
for(int[] t: d)
if(a[t[0]]==a[t[1]]&&a[t[1]]==a[t[2]]&&a[t[2]]==a[t[3]])
return false;
return true;
}
//验证条件其他
static boolean checkagain(int[][] a) {
//一周中,至少有3天所有人都是上班的
int cnt1 = 0;
for(int i=0;i<7;i++)
if(a[0][i]+a[1][i]+a[2][i]+a[3][i]+a[4][i]==5)
cnt1++;
if(cnt1<3)
return false;
//任何一天,必须保证 A B C D 中至少有2人上班
for(int i=0;i<7;i++)
if(a[0][i]+a[1][i]+a[2][i]+a[3][i]<2)
return false;
//B D E 在周日那天必须休息
if(a[1][6]==1||a[3][6]==1||a[4][6]==1)
return false;
//A E 周三必须上班
if(a[0][2]==0||a[4][2]==0)
return false;
//A C 一周中必须至少有4天能见面(即同时上班)
int cnt = 0;
for(int i=0;i<7;i++)
if(a[0][i]==a[2][i]&&a[2][i]==1)
cnt++;
if(cnt<4)
return false;
return true;
}
}
输出是一样的,但后者看起来简洁,也好理解一点。但是迭代更复杂跟难理解对不对。