蓝桥杯-日程表

本文介绍了一种通过逐步筛选和优化来解决复杂排班问题的方法。通过先验条件减少搜索空间,利用两种不同的实现方式(列表筛选与迭代)来找出符合特定约束条件的所有可行排班方案。

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

  • 题目

【编程题】

某保密单位机要人员 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;
        }
}

输出是一样的,但后者看起来简洁,也好理解一点。但是迭代更复杂跟难理解对不对。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值