世界名画陈列馆问题(回溯法)非重复监控分支java

参考原文:https://my.oschina.net/osandy/blog/408006?p=1

原作者的代码并不是  非重复监控, 他实现的是允许重复情况下的回溯法,

我在他的基础上加以限制得到了

世界名画陈列馆问题

的非重复监控的回溯法代码,内容如下:

/**
 * @Author = The Great Ke
 * @description:  世界名画之非重复监视分支
 * @Date: Creat in 18:19 2019/12/15
 * @modified by : The Great Ke
 */
import java.util.Scanner;

/**
 * 问题分析:设陈列馆由m*n个陈列室组成,因为不存在重复监视,所以很多情况下都无解,我们采用的做法是根据m和n的值进行分类讨论。首先,先比较m、n大小,使m始终大于n,方面程序书写。分三种情况讨论:
 *      n=1   这时可以直接写出最优解:
 *           当m mod 3=1时,将哨位置于(1,3k+1);
 *           当m mod 3=0或2时,将哨位置于(2,3k+2),其中k=0、1、…、m/3。
 *      n=2   这种情形下必须2端分别设置2个哨位,他们各监视三个陈列室。那么当m为偶数时问题就无解了。
 *           当m为奇数时,将哨位分别至于(1,4k+3)和(2,4k+1),k=0、1、…、m/4。
 *      n>2  容易验证
 *      当n=3,m=3和n=3,m=4时无解,n=4,m=4有解。
 *     设置哨位时,允许在的n+1行和m+1列设置哨位,但不要求的第n+1行和m+1列陈列室受到监视,
 *     那么当n>=3且m>=5时在不重复监视下有解那么n=3,m=5的不可重复监视问题一定有解。
 *     但是通过验证n=3,m=5的不可重复监视哨位设置问题无解,那么当n>=3且m>=5时在不重复监视下无解。
 */


public class FamousPainting {
    static final int MAX = 1000;
    static int d[][] = { {0,0,0}, {0,0,0}, {0,0,-1}, {0,-1,0}, {0,0,1}, {0,1,0} };
    static int [][]x=new int[MAX][MAX];
    static int [][]y=new int[MAX][MAX];
    static int [][]bestx=new int[MAX][MAX];   //x用来设置当前警卫,y用来表示监控情况,bestx返回最终结果
    static int n, m, best, k = 0, t = 0;   //当前已设置的警卫数为k,受监视的陈列室数为t,当前最少警卫数为best
    static int t1, t2, more;               //判断下界剪枝的条件参数
    boolean p;

    /**
     * 世界名画陈列馆问题(回溯法)
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        System.out.println("请设置陈列馆区域:");
        System.out.print("m: ");
        Scanner sc1=new Scanner(System.in);
        m=Integer.parseInt(sc1.next());
        System.out.print("n: ");
        sc1=new Scanner(System.in);
        n=Integer.parseInt(sc1.next());
        if(n<5 && m<5){compute();
            System.out.println("最少需要"+best+"个警卫!");
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= m; j++)
                    System.out.print( bestx[i][j]+" ");
                System.out.println();
            }}
        else {
            System.out.print("No Solution!");
        }

    }
    static void change(int i, int j) {    //在(i, j)处设置一个警卫,并改变其周围受监控情况
        x[i][j] = 1;
        k++;
        for (int r = 1; r <= 5; r++) {    //在自己本身跟上下左右五个地方设置受控
            int p = i + d[r][1];
            int q = j + d[r][2];
            y[p][q]++;
            if (y[p][q] == 1)
                t++;
        }
    }
    static void restore(int i, int j) {    //撤销在(i, j)处设置的警卫,并改变其周围受监控情况
        x[i][j] = 0;
        k--;
        for (int r = 1; r <= 5; r++) {
            int p = i + d[r][1];
            int q = j + d[r][2];
            y[p][q]--;
            if (y[p][q] == 0)
                t--;
        }
    }
    static void search(int i, int j) {   //回溯搜索
        do {                             //从上到下,从左至右搜索没被监控的位置
            j++;
            if (j > m) {
                i++;
                j = 1;
            }
        } while (!((y[i][j] == 0) || (i > n)));

        if (i > n) {
            if (k < best) {            //更新警卫值
                best = k;
                for (int p = 1; p <= n; p++)
                    for (int q = 1; q <= m; q++)
                        bestx[p][q] = x[p][q];
                return;
            }
        }
        if (k + (t1 - t)/5 >= best)    return;            //警卫数下界 = 还需设置的最少警卫数 + 现有的警卫数
        if ((i < n - 1) && (k + (t2 - t)/5 >= best))    return;   //如果比最优警卫数多的话,就剪去这一分枝
        if (i < n) {                //结点p
            change(i + 1, j);
            search(i, j);            //递归搜索下一个点
            restore(i + 1,j);        //恢复
        }
        if (y[i][j + 1] == 0) {        //结点q
            change(i, j);
            search(i, j);
            restore(i, j);
        }
        if ((j < m) && ((y[i][j + 1] == 0) || (y[i][j + 2] == 0))) {    //结点r
            change(i, j + 1);
            search(i, j);
            restore(i, j + 1);
        }
    }

    static void compute() {
        more = m/4 + 1;
        if (m % 4 == 3)
            more++;
        else if (m % 4 == 2)
            more += 2;
        t2 = m * n + more + 4;
        t1 = m * n + 4;
        best = 65536;
        if (m == 1 && n == 1) {
            System.out.println(1);
            System.out.println(1);
        }
        for (int i = 0; i <= m + 1; i++) {    //在整个外面加上一圈,便于处理边界情况
            y[0][i] = 1;
            y[n + 1][i] = 1;
        }
        for (int i = 0; i <= n + 1; i++) {
            y[i][0] = 1;
            y[i][m + 1] = 1;
        }
        search(1, 0);
    }


}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值