今天突然想不起来回溯怎么写了,于是开始研究经典的回溯题——八皇后问题。但是在看到回溯的原理的时候,脑子有点转不过弯来了。于是决定将回溯过程绘制出来。
Shader "MyShader/SS_10"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color("Color",Color)=(1,1,1,1)
_Size("Size",float)=0.03
_Pos0("Pos0",Vector)=(-1,-1,1,1)
_Pos1("Pos1",Vector)=(-1,-1,1,1)
_Pos2("Pos2",Vector)=(-1,-1,1,1)
_Pos3("Pos3",Vector)=(-1,-1,1,1)
_Pos4("Pos4",Vector)=(-1,-1,1,1)
_Pos5("Pos5",Vector)=(-1,-1,1,1)
_Pos6("Pos6",Vector)=(-1,-1,1,1)
_Pos7("Pos7",Vector)=(-1,-1,1,1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _Size;
float4 _Pos0;
float4 _Pos1;
float4 _Pos2;
float4 _Pos3;
float4 _Pos4;
float4 _Pos5;
float4 _Pos6;
float4 _Pos7;
float4 _Color;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
if((pow((i.uv.x-(_Pos0.x)/2-0.25),2)+pow((i.uv.y-(_Pos0.y)/2-0.25),2))<_Size){
col=_Color;
}else if((pow((i.uv.x-(_Pos1.x)/2-0.25),2)+pow((i.uv.y-(_Pos1.y)/2-0.25),2))<_Size){
col=_Color;
}else if((pow((i.uv.x-(_Pos2.x)/2-0.25),2)+pow((i.uv.y-(_Pos2.y)/2-0.25),2))<_Size){
col=_Color;
}else if((pow((i.uv.x-(_Pos3.x)/2-0.25),2)+pow((i.uv.y-(_Pos3.y)/2-0.25),2))<_Size){
col=_Color;
}else if((pow((i.uv.x-(_Pos4.x)/2-0.25),2)+pow((i.uv.y-(_Pos4.y)/2-0.25),2))<_Size){
col=_Color;
}else if((pow((i.uv.x-(_Pos5.x)/2-0.25),2)+pow((i.uv.y-(_Pos5.y)/2-0.25),2))<_Size){
col=_Color;
}else if((pow((i.uv.x-(_Pos6.x)/2-0.25),2)+pow((i.uv.y-(_Pos6.y)/2-0.25),2))<_Size){
col=_Color;
}else if((pow((i.uv.x-(_Pos7.x)/2-0.25),2)+pow((i.uv.y-(_Pos7.y)/2-0.25),2))<_Size){
col=_Color;
}
return col;
}
ENDCG
}
}
}
绘制8个棋子就要声明8个参数,感觉太笨了,或许用ComputeShader改编一下会美观一点。不过,今天的主题不是这个,还是先把回溯算法搞清楚。
需要编写控制脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SS_10 : MonoBehaviour
{
private Material mat;
public float cd = 0.3f;
public Material Mat
{
get
{
if (mat == null)
{
mat = GetComponent<MeshRenderer>().material;
}
return mat;
}
set
{
mat = value;
}
}
private void Start()
{
Draw();
}
void Draw()
{
Clear();
int n = 8;
var x = new int[n + 1];
for (int i = 0; i < n; i++)
{
x[i] = -1;
}
StartCoroutine(IE_DrawProcess(n, x));
//StartCoroutine(IE_ShowQueens(GetQueen(n, x)));
}
//展示所有满足条件的结果
IEnumerator IE_ShowQueens(List<Vector2Int[]> _list)
{
for (var i = 0; i < _list.Count; i++)
{
for (var j = 0; j < _list[i].Length; j++)
{
SetMatValue(j, _list[i][j]);
yield return new WaitForSeconds(0.1f);
}
yield return new WaitForSeconds(cd);
Clear();
yield return new WaitForSeconds(cd);
}
}
//修改材质球,以显示棋子位置
void SetMatValue(int _index, Vector2Int _v)
{
Mat.SetVector("_Pos" + _index, new Vector4(_v.x, _v.y, 0, 0));
}
//清空所有棋子
void Clear()
{
for (var i = 0; i < 8; i++)
{
SetMatValue(i, new Vector2Int(-1, -1));
}
}
//获取所有满足条件的结果
List<Vector2Int[]> GetQueen(int n, int[] x)
{
List<Vector2Int[]> queens = new List<Vector2Int[]>();
int k = 0;
while (k >= 0)
{
x[k]++;
while (x[k] < n && !CheckQueen(k, x))
{
x[k]++;
}
if (x[k] < n && k == n - 1)
{
var ans = new Vector2Int[8];
for (int i = 0; i < n; i++)
{
ans[i] = new Vector2Int(i, x[i]);
}
queens.Add(ans);
}
else if (x[k] < n && k < n - 1)
{
k++;
}
else
{
x[k] = -1;
k--;
}
}
return queens;
}
//查看k皇后是否满足约束条件
bool CheckQueen(int k, int[] x)
{
for (int i = 0; i < k; i++)
if (x[i] == x[k] || Mathf.Abs(x[i] - x[k]) == Mathf.Abs(i - k)) //满足在同一行或同一条斜线(斜率相同)
return false;
return true;
}
//展示回溯的过程
IEnumerator IE_DrawProcess(int n, int[] x)
{
int k = 0;//从 皇后0 开始放
while (k >= 0)
{
//摆放第k列的皇后, 摆在第x[k]+1行(x[k]初始为-1)
x[k]++;
//对皇后k进行检测,直到不发生冲突或x[k]越界 ;检测到第7行时,下一次检测第8行后就会越界
while (x[k] < n && !CheckQueen(k, x))
{
x[k]++;//检测下一行
yield return new WaitForSeconds(0.01f);
}
SetMatValue(k, new Vector2Int(k, x[k]));
//8个棋子都放上去了的时候,如果x[k]==n说明放到了棋盘外;反之结果是正确的
if (x[k] < n && k == n - 1)
{
mat.SetColor("_Color", Color.green);
yield return new WaitForSeconds(2);
mat.SetColor("_Color", Color.white);
}
else if (x[k] < n && k < n - 1)//若皇后还没有摆放完,就摆放下一个皇后k = k+1
{
k++;
}
//发生了越界,要进行回溯【核心】
else
{
x[k] = -1;//将该列清空
k--;//回到上一列,它会增加一行后继续检测【比如第k-1列的当前行是3,从k回溯到k-1后,会从第4行开始检测,当第8行也检测失败时,继续向上回溯】
}
yield return new WaitForSeconds(cd);
}
}
}
先看看回溯过程:
用shader绘制出来了整个回溯过程,应该不会再忘了吧。
另外还能看看所有满足条件的结果: