【算法设计与分析基础】关灯游戏

本文探讨了经典的关灯游戏,深入分析了其背后的算法设计,并结合高斯消元方法提供了解决方案。通过Java实现,阐述了如何在实际编程中应用这些理论。
摘要由CSDN通过智能技术生成

① R.java  用于存储一些全局变量

package lightoff;


public class R {
	/*
 	public static int lightCondition[][] = {
		{1,0,0,1,1},
		{1,0,0,1,1},
		{1,0,0,1,1},
		{1,0,0,1,1},
	};
	 */
	public static int lightCondition[][] = {
		{1,1,1,1},
		{1,1,1,1},
		{1,1,1,1},
		{1,1,1,1},
	};
	
	//灯面板有 n x m 个灯
	public static int n = lightCondition.length;//行
	public static int m = lightCondition[0].length;//列
			
	public static int rect_Height = 30;//形成的矩阵的高
	public static int rect_Width = 30;//形成的矩阵的宽
	public static int[][] lightswitch=new int[n][m];//哪些开关按过
	public static int frame_Height = 240+ n * rect_Height;//窗口的高度
	public static int frame_Width = 240 + m * rect_Width;//窗口的宽度
	public static Gauss g ;//高斯消元算法类
	public static LightPanel lP = new LightPanel();//n x m 灯面板
	public static ButtonPanel bP = new ButtonPanel();//按钮面板
	public static SurfaceView sv;//关灯游戏界面
	public static Demo d;//演示界面
	public static double g_sleeptime = 0.3;//生成开关影响表的暂停时间
	public static double l_sleeptime = 0.1;//转化成上三角矩阵的暂停时间
	public static double r_sleeptime = 0.5;//结果分析的暂停时间
	public static boolean demo = true;//是否需要演示。如果不需要请把这里设为false

	//矩阵面板左上角顶点的坐标
	public static int rect_x = 50;
	public static int rect_y = 50;
	/**
	 * 重绘 n x m 面板
	 * @param _n 行
	 * @param _m 列
	 */
	public static void rewriteLightCondition(int _n,int _m){
		n=_n;
		m=_m;
		if(n>21){
			rect_Height = rect_Width = 20;
		}
		else{
			rect_Height = rect_Width = 30;
		}
		frame_Height = 240+ n * rect_Height;//窗口的高度
		frame_Width = 240 + m * rect_Width;
		lightCondition=new int[n][m];
		lightswitch=new int[n][m];
		
		int i,j;
		for(i=-1;++i<n;){
			for(j=-1;++j<m;){
				lightCondition[i][j]=1;//(int)(Math.random()*100)%2;
			}
		}
		sv.setSize();
		
	}
	/**
	 * 暂停一段时间
	 * @param i 单位秒
	 */
	public static void sleep(double i){
		try {
			Thread.sleep((int)(i*(double)1000));
		} catch (InterruptedException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		};
	}
}


②  GameBegin.java   main函数所在位置,创建图形界面。

package lightoff;

import java.lang.reflect.InvocationTargetException;

public class GameBegin {
	public static void main(String[] args){
		try {
			javax.swing.SwingUtilities.invokeAndWait(new Runnable(){
				public void run(){
				     R.sv = new SurfaceView();
				}
			});
		} catch (InvocationTargetException | InterruptedException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		
	}
}


③ Gauss.java  关灯游戏的算法文件,类似高斯消元法。

package lightoff;

class BreakPoint{
	int r;//断点所在的行
	int c;//当前是0还是1
	public BreakPoint(){
		r = -1;
		c = -1;
	}
}

public class Gauss {
	int num = R.n * R.m;
	int[] ls = new int[num];
	int[][] gauss = new int[num][num];
	int[] answer =  new int[num];
	BreakPoint[] bp = new BreakPoint[num];
	boolean exist = false;
	int n = 0;
	/**
	 * 建立开关影响的表,做高斯消元求解
	 */
	public Gauss(){
		
	}
	public void setgauss(){
		if(R.demo){
		    R.d.step_next(0);
		    R.d.settitle("根据开关的影响建立高斯消元表");
		}
         /// 1. 开关的影响:建立高斯消元表  /
		int i,j,col;
		//遍历 n x m 面板
		for(i=-1;++i<R.n;){
			for(j=-1;++j<R.m;){
				col = i*R.m+j;
				ls[col] = R.lightCondition[i][j];//高斯消元表最右边的常数
				//一个开关造成的影响最多五个点,最少三个点
				//第一个点:自己
				gauss[col][col] = 1;
				//第二个点:左边的点
				if(inthePanel(i,j-1)){
					gauss[i*R.m+j-1][col] = 1;
				}
				//第三个点:下边的点
				if(inthePanel(i-1,j)){
					gauss[(i-1)*R.m+j][col] = 1;
				}
				//第四个点:上边的点
				if(inthePanel(i+1,j)){
					gauss[(i+1)*R.m+j][col] = 1;
				}
				//第五个点:右边的点
				if(inthePanel(i,j+1)){
					gauss[i*R.m+j+1][col] = 1;
				}
				if(R.demo){
				    R.d.setcol(col);
				    R.d.rd();
				    R.sleep(R.g_sleeptime);
				}
				
			}
		}
	}
	/**
	 * 判断该坐标是否在灯面板内
	 * @param x 行坐标
	 * @param y 列坐标
	 * @return 布尔类型
	 */
	boolean inthePanel(int x, int y){//x是行,y是列
		return x >= 0 && y >= 0 && x < R.n && y < R.m;
	}
	/**
	 * 进行异或运算
	 * @param a 数字1
	 * @param b 数字2
	 * @return a 异或 b
	 */
	int xor(int a,int b){
		return a^b;
	}
	/**
	 * 交换两个数字
	 * @param x1 第一个数字的行坐标
	 * @param x2 第二个数字的行坐标
	 * @param y 它们有个共同的列坐标
	 */
	void swap(int x1,int x2,int y){
		gauss[x1][y]^=gauss[x2][y];
		gauss[x2][y]^=gauss[x1][y];
		gauss[x1][y]^=gauss[x2][y];
	}
	/**
	 * 交换两行
	 * @param r1 其中一行
	 * @param r2 另外一行
	 */
	void swap(int r1,int r2){
		int i;
		for(i=-1;++i<num;){
			swap(r1,r2,i);
		}
		ls[r1]^=ls[r2];
		ls[r2]^=ls[r1];
		ls[r1]^=ls[r2];
	}
	/**
	 * r1行与r2行异或以后赋给r2行
	 * @param r1 某一行
	 * @param r2 另外一行
	 */
	void add(int r1,int r2){
		int i;
		for(i=-1;++i<num;){
		    	gauss[r2][i]=xor(gauss[r1][i],gauss[r2][i]);
		}
		ls[r2]=xor(ls[r1],ls[r2]);
	}
	void ga(){
		if(R.demo){
		    R.d.step_next(1);
		    R.d.settitle("转化成上三角形矩阵");
		}
		int i,j;
		for(i=-1;++i<num;){
			if(R.demo){
			    R.d.col_rd(i);
			}
			j = i;
			if(gauss[i][i]==0){
				for(;++j<num;){
					if(gauss[j][i]==1){
						swap(i,j);
						if(R.demo){
						    //以下两行表示把过程绘制在一个面板里面/
						    R.d.swap_rd(i,j);
						    R.d.rd();
						    R.sleep(R.l_sleeptime);
						    //
						}
						break;
					}
				}
			}
			for(;++j<num;){
				if(gauss[j][i]==1){
					add(i,j);
					if(R.demo){
				        //以下两行表示把过程绘制在一个面板里面/
					    R.d.add_rd(i,j);
					    R.d.rd();
					    R.sleep(R.l_sleeptime);
                        //
					}
				}
			}
			if(R.demo){
	     	    //以下三行表示把过程绘制在一个面板里面/
			    R.d.swap_rd(-1,-1);
			    R.d.add_rd(-1,-1);
			    R.d.rd();
			    R.sleep(R.l_sleeptime);
                //
			}
		}
	}
	/**
	 * 对值进行分析(X表示0或1都可以)
	 * 0 0 0 => X
	 * 0 0 1 => 无解
	 * 0 1 0 => 无解
	 * 0 1 1 => X
	 * 1 0 0 => 0
	 * 1 0 1 => 1
	 * 1 1 0 => 1
	 * 1 1 1 => 0
	 * @param a gauss表中对角线上的值
	 * @param b gauss表中对角线 右边一行的值乘以对应的answer 之和 % 2
	 * @param c 该行上的常数列
	 * @param i 该行行数
	 * @return 分析后的值
	 */
	int analyze(int a,int b,int c,int i){
		int col = 0;
		if(R.demo){
			R.d.setabc(a, b, c);
		}
		if(a==1){
			return Math.abs(b-c);
		}
		else if(a==0){
			if(b==c){
				// 在 X 这里设置个断点
				col = getbp(i);
				if(col == -1){
					bp[n] = new BreakPoint();
					bp[n].r = i;
					bp[n].c = 0;
					n++;
					return 0;
				}
				return col;
			
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值