手机站
网通分站
电信主站
密 码:
用户名:
当前位置 : 主页>程序设计>C/C++>列表

数学和程式 一道游戏题目的快速解法

来源:互联网 作者:west263.com 时间:2008-02-23
西部数码-全国虚拟主机10强!40余项虚拟主机管理功能,全国领先!双线多线虚拟主机南北访问畅通无阻!免费赠送企业邮局,.CN域名,自助建站480元起,免费试用7天,满意再付款! P4主机租用799元/月.月付免压金!

  题目:

  有十个开关等间距排成一线,每个开关对应其上方的一盏灯(十盏灯也排成一线)。每按动一下开关,能够使对应的灯改变状态(原来亮着的将熄灭,原来熄灭的将被点亮)。

  但是,由于开关之间的距离很小,每次按动开关时,相邻的一个开关也将被按动。例如:按动第5个开关,则实际上第4、5、6个开关都被按动。而按动靠边的第1个开关时,第1、2个开关都被按动。并且,无法只按动最靠边的一个开关。

  现在给出十盏灯的初始的状态和目标状态,需要计算:从初始状态改变到目标状态所需要的最少操作次数。

  函数接口:

  int MinChange(const int Start[],const int End[]);

  其中:Start表示了初始状态,End表示了目标状态。表示状态的数组(Start和End)中,若某元素为0表示对应的灯亮着,否则表示对应的灯没有亮。调用函数时确保Start和End数组长度均为10,并确保有解。

  看了很多人的解法都是用循环遍历来判断是否达到最后需要,但是假如和线形代数结合的话,就有一种很快速的解法。

  约定:以下所用的‘ ’号都是‘异或’的运算。

  先简化一下,假设有四个灯,初始状态s0~s3,目标状态是e0~e3,转换一次状态就是和1进行异或运算一次,所以状态转移矩阵为:

  (s0,s1,s2,s3) k0*(1,1,0,0) k1*(1,1,1,0) k2*(0,1,1,1) k3*(0,0,1,1)=(e0,e1,e2,e3);

  其中k(n)表示第n个开关所翻动的次数。并且,注意异或运算中a b b=a,所以,某个开关翻动偶数次的效果相当于没有翻动,翻动奇数次的效果相当于翻动一次;又由于异或运算满足交换律,所以翻动的顺序没有影响。综上每个开关翻动的次数只有1次或0次就足够了。

  设m(n)=s(n) e(n),注意异或运算中的'-'也就是' ',所以解线性方程组:

  k0 k1 =m1;

  k0 k1 k2 =m2;

  k1 k2 k3=m3;

  k2 k3=m4;

  假设解存在,就能够算出通解(k0,k1,k2,k3),再统计出通解中1的个数,就是所需要翻动的次数了。并且还能够知道哪些开关需要拨动,比如算出解是(1,0,1,0)就是第0和2个开关需要拨动一次。

  因此针对本题目的10个灯泡,本人已算出这10元线性方程组的通解:

  k0=m0 m2 m3 m5 m6 m8 m9;

  k1=m2 m3 m5 m6 m8 m9;

  k2=m0 m1;

  k3=m3 m0 m1 m5 m6 m8 m9;

  k4=m5 m6 m8 m9;

  k5=m4 m3 m0 m1;

  k6=m6 m4 m3 m0 m1 m8 m9;

  k7=m8 m9;

  k8=m7 m6 m4 m3 m0 m1;

  k9=m9 m7 m6 m4 m3 m0 m1;

  和上面相同,m(n)为开始状态和目标状态的每位异或。至于是否存在解,本人已将次系数矩阵化简为对角矩阵,能够看到系数矩阵的秩(Rank)和未知数的个数相等,所以无论是任何的输入和输出变换都能找到唯一解。

  本人程式如下:

  int MinChange(const int Start[],const int End[]){

  int m[10];

  int k[10];

  int res=0;

  int i,j,n;

  for(i=0;i<10;i ){

  m[i]=Start[i]^End[i]; /* m[]=Start[] XOR End[] */

  }

  /* calculate roots */

  k[0]=m[0]^m[2]^m[3]^m[5]^m[6]^m[8]^m[9];

  k[1]=m[2]^m[3]^m[5]^m[6]^m[8]^m[9];

  k[2]=m[0]^m[1];

  k[3]=m[3]^m[0]^m[1]^m[5]^m[6]^m[8]^m[9];

  k[4]=m[5]^m[6]^m[8]^m[9];

  k[5]=m[4]^m[3]^m[0]^m[1];

  k[6]=m[6]^m[4]^m[3]^m[0]^m[1]^m[8]^m[9];

  k[7]=m[8]^m[9];

  k[8]=m[7]^m[6]^m[4]^m[3]^m[0]^m[1];

  k[9]=m[9]^m[7]^m[6]^m[4]^m[3]^m[0]^m[1];

  /* count for switch times */

  for(j=0;j<10;j ){

  if(k[j]) res ;

  }

  /* display k(n); */

  for(n=0;n<10;n ) printf("%d,",k[n]);

  return res;

  }

  测试主程式:

  main(){

  int A[10]={1,1,1,0,0,1,0,1,1,0};

  int B[10]={0,0,1,1,0,0,1,1,1,1};

  int C;

  C=MinChange(A,B);

  printf("**%d**",C);

  }

  显示结果为:

  1,0,0,0,1,1,1,1,0,1,**6**

  就是假如要把状态A转为状态B,需要把第0,4,5,6,7,9号开关翻动一次,共6次。

  测试验证结果正确:)

  当然,此做法也有一个缺点,就是当灯的个数改变时,就要重新设定线形方程组的特解形式。




文章整理:西部数码--专业提供域名注册虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!

热点关注
IDC资讯 虚拟主机 域名注册 托管租用 vps主机 智能建站
网站运营 建站经验 策划盈利 搜索优化 网站推广 免费资源
网站联盟 联盟新闻 联盟介绍 联盟点评 网赚技巧
行业资讯 业界动态 搜索引擎 网络游戏 门户动态 电子商务 广告传媒
网络编程 Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术 Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷 Internet Explorer
网页制作 FrontPages Dreamweaver Javascript css photoshop fireworks Flash
程序设计 Java技术 C/C++ VB delphi
网络知识 网络协议 网络安全 网络管理 组网方案 Cisco技术
操作系统 Win2000 WinXP Win2003 Mac OS Linux FreeBSD
返回首页 |关于我们 | 联系我们 | 付款方式 | 创业联盟 | 价格总览 | 资讯中心 | 友情链接 | 网站地图 | 招贤纳士 | RSS