【无标题】

《关于拼图游戏可解性证明》

问题及设定
1、将1~nn个数随机生成一个的排列,任取第N个数作为空块(其目标值为N0),然后将这nn个数(包括空块)填入n阶方阵中,请问:这个拼图可以还原吗?
2、设去掉N0后,排列的逆序数为Inv,N在nn方阵中的行数为r0,即初始空块所在的行数(从上往下数)。N0在nn方阵中的行数为rt,即拼图完成时空块所在的行数。
3、举例:随机生成一个3阶方阵数字:361592784,随机拿掉第6个方块,这个方块(空块)在3阶方阵中位于第2行,即r0=2,而它本身的数字是2,在拼图完成时应位于第一行,所以rt=1。上述排列去掉2后的逆序数等于:0+0+2+1+0+1+1+5=10,即Inv=10,下面探讨任意n阶生成时,是否都可解。

关键性质
1、任意交换相邻的两个数,排列的逆序数的奇偶性发生改变。
2、上下移动一个块,相当于相邻交换的操作做了n-1次。
3、只有方块的上下移动才改变逆序数,左右移动不改变逆序数。
4、拼图成功时每个块按顺序排列,其逆序数为0,故:目标状态为偶排列。
可解性推导
当 n 为奇数:
每次上下移动不改变 Inv 的奇偶性(n-1为偶),所以无论空块移动多少次,Inv 的奇偶性始终保持不变。目标状态要求 Inv 为偶数,因此 Inv初始 必须为偶数。
当 n 为偶数:
每次上下移动会改变 Inv 的奇偶性(由关键性质2,n-1为奇)。空块需要 |r0-rt| 次上下移动,这会使 Inv 的奇偶性改变 |r0-rt| 次。最终要得到偶排列(逆序数 0),故初始奇偶性加上改变次数必须为偶,即:Inv 的奇偶性 + |r0-rt| 的奇偶性 = 偶。
由于 |r0-rt| 与 r0-rt 同奇偶,条件可写为:Inv + (r0 - rt) 为偶数。
结论
1、当n为奇数时:Inv必须为偶数,拼图才可解。
2、当n为偶数时,Inv+r0-rt必须为偶数,拼图才可解。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

关键代码如下:


    //初始化棋盘   
    private void MakeBox(string fn,int N)     //产生盒子,N=3,5,8
        {
            mButton = new AButton[N * N];
            int i,r,c;
            int w;
            Image img;
                       
            this.Controls.Clear();   //移除之前的所有控件
           
            w = 600 / N;

            try
            {
                img = Image.FromFile(fn);
                for (i = 0; i <N*N; i++)
                {
                    r = i / N; c = i % N;
                    mButton[i] = new AButton(i+1, N, CaptureImage(img, w, w, c * w, r * w));                         
                    this.Controls.Add(mButton[i]);
                }
            }
            catch (Exception e)
            {
                if (MessageBox.Show(e.ToString() + "\n   错误!  是否继续(Y/N)?", "错误", MessageBoxButtons.YesNo) == DialogResult.No)
                {
                    Application.Exit();
                }
            }
         }

        public void InitA(int n)   //初始化数组 和 图片 最后一个块必须空掉。数组置为0
        {
            A = new int[n, n];
            int[] b = new int[n*n];      //随机化的一维数组。
            Random   rand = new Random();
            int i, j,k;
            int r, c;
            bool TF;
            int Inv;

            //以下数字表示了图片的索引号。也是在棋盘中的位置。

            r = 0;c = 0;
           //生成不重复的随机数  1~n*n
            b[0] = rand.Next(n*n) + 1;   
            i = 1;
            while (i<n*n)            
            {
                do
                {
                    k = rand.Next(n*n) + 1;
                    j = 0;TF = false;
                    while (j < i & b[j] != k) j++;
                    if (j == i) TF = true;
                 } while (!TF);
                b[i] = k;
                i = i + 1;
            }

            //随机去掉一个块,作为空块,但要先记住它 。2026
            int RemoveN = rand.Next(n * n);  // 0~n*n-1

          
            for (i = 0; i < n; i++)
                for (j = 0; j < n; j++)
                {
                    A[i, j] = b[i * n + j ];
                    if (i * n + j == RemoveN)    //把随机去掉的块置0,并记录空块的位置
                    {
                        RememberN = A[i, j];      // 1~n*n  
                        A[i, j] = 0;
                        r = i;
                        c = j;
                    }
                 }

            int r0 = n - r; //空块从下往上数的行数,从1开始数。
            rt =n-(RememberN-1)/ n; //目标行数。2026
            Inv=  GetInverse(A);  //计算逆序数
                         
            if (n % 2 == 1 )
            {
                if (Inv % 2 == 1)  //n为奇数时,逆序数必须为偶数才能完成。否则不可能完成拼图。
                {
                   this.Text =$"{n}阶,逆序数为奇数,不可能完成的拼图!";
                }
                else
                {                    
                    this.Text = "拼图游戏";
                }
                
            }
            else //n为偶数时,逆序数加上空块所在行数必须与目标行数同奇偶性才能完成。否则不可能完成拼图。
            {
                if ((Inv + r0-rt ) % 2 !=0)  
                {                   
                   this.Text =$"{n}阶,逆序数加上空块行数-目标行数 应为偶性,否则不可能完成拼图!";
                }
                else
                {
                   this.Text = "拼图游戏";
                }

            }
             
            //初始化AButton的位置,mButton的索引号从0到n*n-1,而A数组的值从1到n*n-1,0表示空块。

            for (i = 0; i < n; i++)
                for (j = 0; j < n; j++)
                    if (A[i, j] >0) mButton[A[i, j] - 1].Location=new Point( j * 600/n,i*600/n);

            mButton[RememberN-1].Location = new Point(6000, 6000);   //把空块甩得远远地!:)
             
            TotalStep = 0;
        }


        public static int GetInverse(int[,] A)// 计算逆序数,先把二维数组转成一维数组,去掉0后计算逆序数。2026
        {
            int i, j, Inv;
            Inv = 0;
            int[] cc = A.Cast<int>().Where(x => x != 0).ToArray();

            for (i = 0; i < cc.Length - 1; i++)
                for (j = i + 1; j < cc.Length; j++)
                    if (cc[i] > cc[j]) Inv ++;
            return Inv;
        }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值