1. 背景

  中午下楼去吃饭,电梯里看到有人在玩数独,之前也玩过,不过没有用程序去解过,萌生了一个想法,这两天就一直想怎么用程序去解一个数独。要去解开一个数独,首先要先了解数独的游戏规则,这样才能找到对应的算法去解开。以下是本人用Java语言对数独进行的解析,代码只是抛砖引玉,欢迎大神们给指点指点。

2. 数独知识

  数独是源自18世纪瑞士的一种数学游戏。是一种运用纸、笔进行演算的逻辑游戏。玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫(3*3)内的数字均含1-9,不重复。
  数独盘面是个九宫,每一宫又分为九个小格。在这八十一格中给出一定的已知数字和解题条件,利用逻辑和推理,在其他的空格上填入1-9的数字。使1-9每个数字在每一行、每一列和每一宫中都只出现一次,所以又称“九宫格”。
 
水平方向有九横行,垂直方向有九纵列的矩形,画分八十一个小正方形,称为九宫格(Grid),如图一所示,是数独(Sudoku)的作用范围。

三行与三列相交之处有九格,每一单元称为小九宫(Box、Block),简称宫,如图四所示

更多关于数独的知识可以查看百度百科。

3. 生成随机数独

  在解开一个数独之前,首先要知道数独是怎么生成的,接下来先随机生成一个9*9的数独。

  生成思路:使用嵌套for循环,给每个格子填数,这个格子中的数必是1-9中的某一个数字,在填第n个格子时,要排除行、列、宫中已经存在的数字,在剩下的数字中随机选一个,如果排除掉行、列、宫中的数字后,已经没有可选数字了,说明这个数独生成错了,while循环重新开始生成,直到生成一个可用的数独。这个地方用到了Set集合及集合中的方法,以下是生成数独的代码。

package com.woasis.demo;

import java.util.*;

/**
* 数独
* 1 3 3 4 5 6 7 8 9
* 1. [1, 2, 3, 4, 5, 6, 7, 8, 9]
* 2. [1, 2, 3, 4, 5, 6, 7, 8, 9]
* 3. [1, 2, 3, 4, 5, 6, 7, 8, 9]
* 4. [1, 2, 3, 4, 5, 6, 7, 8, 9]
* 5. [1, 2, 3, 4, 5, 6, 7, 8, 9]
* 6. [1, 2, 3, 4, 5, 6, 7, 8, 9]
* 7. [1, 2, 3, 4, 5, 6, 7, 8, 9]
* 8. [1, 2, 3, 4, 5, 6, 7, 8, 9]
* 9. [1, 2, 3, 4, 5, 6, 7, 8, 9]
*
*/
public class Sudoku { public static void main(String[] args) {
boolean flag = true;
while (flag) {
try {
start();
flag = false;
} catch (ArithmeticException e) {
System.out.println(e);
}
}
} /**
* 开始生成数独
*/
private static void start(){
int[][] source = new int[9][9];
//第i行
for (int i=0; i<9; i++){
// 第i行中的第j个数字
for (int j=0; j<9; j++){
//第i行目前的数组
int[] row = Arrays.copyOf(source[i], j);
int[] column = new int[i];
for (int k=0; k<i; k++){
column[k] = source[k][j];
}
//所在宫
List<Integer> palaceList = new ArrayList<>();
//取整,获取宫所在数据
int palaceRow = i/3;
int palaceColumn = j/3;
for (int m=0; m<3; m++){
for (int n=0; n<3; n++){
palaceList.add(source[palaceRow*3+m][palaceColumn*3+n]);
}
}
source[i][j] = getNumber(row, column, palaceList.stream().mapToInt(Integer::intValue).toArray());;
}
} //打印随机生成的数独数组
for (int i=0; i<source.length; i++){
System.out.println(Arrays.toString(source[i]));
}
} /**
* 从即没有在行也没有在列中,选出一个随机数
* @param row
* @param column
* @return
*/
private static int getNumber(int[] row, int[] column, int[] palace ){
//数组合并,并去重,使用Set集合
Set<Integer> mergeSet = new HashSet<>();
for (int i=0; i<row.length; i++){
mergeSet.add(row[i]);
}
for (int j=0; j<column.length; j++){
mergeSet.add(column[j]);
} for (int k=0; k<palace.length; k++){
mergeSet.add(palace[k]);
}
Set<Integer> source = new HashSet<>();
for (int m=1; m<10; m++){
source.add(m);
}
//取差集
source.removeAll(mergeSet);
int[] merge = source.stream().mapToInt(Integer::intValue).toArray();
//随机返回一个下标
return merge[getRandomCursor(merge.length)];
} /**
* 获取一个随机下标
* @param length
* @return
*/
public static int getRandomCursor(int length) {
return Math.abs(new Random().nextInt())%length;
}
}

如下图是代码执行后生成的随机数独,行、列、宫中都是1-9个数字,没有重复。

4. 数独的解析

  数独已经可以生成了,现在就对数独进行解析,首先声明一下,接下来的方法可能对一些数独是解不开的,解开数独不是唯一目的,而是在解析数独中对一些Java知识进行回顾和学习。采用的是隐形唯一候选数法,什么是唯一候选数法呢,就是某个数字在某一行列宫格的候选数中只出现一次,就是这个格子只有一个数可选了,那这个格子里就只能填这个数,这就是唯一候选数法,其实也是排除法。参照的这篇文章进行的一次数独解析,数独解题方法大全,可以参考学习一下。

  解题思路:

  1. 要解析的数独,与数独对应的隐形数组;
  2. 排除掉隐形数组中的数字,哪些数字需要排除呢,就是数独中已有的数字,要排除该数字所在的行、列、宫。例如,如下图R4C4格是2,则R4行、C4列以及2所在的宫除了R4C4格子之外,其余的候选数中都不能有2这个数字了。

  3. 排除一次完成后,看剩下的隐形数组中有没有剩下的单个数,如果有则剩下的这个候选数字就是该位置所要填的数字,有的话需要递归一次2步骤;查看行中有没有唯一的单数,如果有递归一次2步骤;查看列中有没有唯一的单数,如果有递归一次2步骤。

  4. 排除以部门隐形数字之后,有一些数字是不好排除的,就是一些对数,对数就是在一个宫两个格子,候选数字都是AB,要么这个格子是A要么另一个格子是B。到这个地方之后不好排除,只能用试探法,假如一个格子是A,那么另一个格子是B,这样去试探,如果试探一次后发现试探的对的,那么就确认这种试探是可行的,如果不对,则数值对换。

  5. 步骤4试探对之后,再从步骤2进行递归,直到获得最终解。

以下是完整代码:

  其中demo中解析的数独就是数独解题方法大全中隐形唯一候选数法中的一个例子。

 package com.woasis.demo;

 import java.util.*;

 public class SudokuCrack {
public static void main(String[] args) {
//生成候选数字表,9行9列,每个格子有9个数字
int[][][] candi = new int[9][9][9];
//初始化候选数字表
for (int i=0; i<9; i++){
for (int j=0; j<9; j++){
candi[i][j] = new int[]{1,2,3,4,5,6,7,8,9};;
}
}
int[][] sudo = {
{0,0,9,6,0,0,0,3,0},
{0,0,1,7,0,0,0,4,0},
{7,0,0,0,9,0,0,8,0},
{0,7,0,0,8,0,5,0,0},
{1,0,0,0,4,0,0,2,0},
{0,2,0,0,1,0,9,0,0},
{5,0,0,0,0,9,0,0,0},
{6,0,0,0,0,3,0,0,2},
{4,0,0,0,0,0,0,0,1}
}; if (isOkSudo(candi, sudo)){
System.out.println("校验是不是一个合法数独:是");
}else {
System.out.println("校验是不是一个合法数独:不是");
return;
} crack(candi, sudo); //获取隐形数组中两个相等的数
List<CandiInfo> equalCandi = getEqualCandi(candi,sudo); //获取其中一个进行试探。
for (CandiInfo info : equalCandi){ //获取坐标
String[] location = info.location.split("\\|");
String[] ALocation = location[0].split("-");
int aRow = Integer.parseInt(ALocation[0]);
int aColumn = Integer.parseInt(ALocation[1]);
String[] BLocation = location[1].split("-");
int bRow = Integer.parseInt(BLocation[0]);
int bColumn = Integer.parseInt(BLocation[1]);
//获取数据
int[] data = info.nums.stream().mapToInt(Integer::intValue).toArray(); System.out.println("开始进行试探:data="+data[0]+", "+data[1]+" 位置:"+aRow+"-"+aColumn+", "+bRow+"-"+bColumn); if(isRight(candi, sudo,aRow, aColumn, bRow, bColumn, data[0], data[1])){
modifySudoAndCandi(candi, sudo, aRow, aColumn, data[0]);
modifySudoAndCandi(candi, sudo, bRow, bColumn, data[1]);
}else{
modifySudoAndCandi(candi, sudo, aRow, aColumn, data[1]);
modifySudoAndCandi(candi, sudo, bRow, bColumn, data[0]);
}
crack(candi, sudo);
} System.out.println("解析完成:");
for (int i=0; i<9; i++){
System.out.println(Arrays.toString(sudo[i]));
}
} /**
* 试探这样的组合是否正确
* @param candi
* @param sudo
* @param aRow
* @param aColumn
* @param bRow
* @param bColumn
* @param data0
* @param data1
* @return
*/
private static boolean isRight(int[][][] candi, int[][] sudo, int aRow, int aColumn, int bRow, int bColumn, int data0, int data1){
int[][][] deepCandiCopy = new int[9][9][9];
for (int i=0; i<9; i++){
deepCandiCopy[i] = candi[i].clone();
}
int[][] deepSudoCopy = new int[9][9];
for (int i=0; i<9; i++){
deepSudoCopy[i]= sudo[i].clone();
}
modifySudoAndCandi(deepCandiCopy, deepSudoCopy, aRow, aColumn, data0);
modifySudoAndCandi(deepCandiCopy, deepSudoCopy, bRow, bColumn, data1); crack(deepCandiCopy, deepSudoCopy); return isOkSudo(deepCandiCopy,deepSudoCopy);
} /**
* 隐藏数法解析数独
* @param candi 隐藏数数组
* @param sudo 要解的数独
*/
private static void crack(int[][][] candi, int[][] sudo){ eliminateCandidateNumbers(candi, sudo); //一轮结束后,查看隐形数组里有没有单个的,如果有继续递归一次
boolean flag = false;
for (int k=0; k<9; k++){
for (int q=0; q<9; q++){
int f = sudo[k][q];
if (f == 0){
int[] tmp = candi[k][q];
Set<Integer> s = new HashSet<>();
for (int t=0; t<tmp.length; t++){
if (tmp[t]>0){
s.add(tmp[t]);
}
}
//说明有单一成数据可以用的
if (s.size() == 1){
flag = true;
modifySudoAndCandi(candi, sudo, k, q, s.stream().mapToInt(Integer::intValue).toArray()[0]);
}
}
}
}
//如果有确定的单个数,进行递归一次
if (flag){
crack(candi, sudo);
}
//查看行有没有唯一数字,有就递归一次
flag = checkRow(candi, sudo);
if (flag){
crack(candi, sudo);
}
//查看列有没有唯一数字,有就递归一次
flag = checkColumn(candi, sudo);
if (flag){
crack(candi, sudo);
}
} /**
* 剔除数组中的候选数字,剔除行、列、宫
* @param candi
* @param sudo
*/
private static void eliminateCandidateNumbers(int[][][] candi, int[][] sudo){
for (int i=0; i<9; i++){
for (int j=0; j<9; j++){
int num = sudo[i][j];
//剔除备选区数字
if (num>0){
candi[i][j] = new int[]{0,0,0,0,0,0,0,0,0};
for (int m=0; m<9; m++){
int[] r = candi[i][m];
r[num-1] = 0;
int[] c = candi[m][j];
c[num-1] = 0;
}
//摒除宫里的唯一性
//取整,获取宫所在数据
int palaceRow = i/3;
int palaceColumn = j/3;
for (int m=0; m<3; m++){
for (int n=0; n<3; n++){
int[] p = candi[palaceRow*3+m][palaceColumn*3+n];
p[num-1] = 0;
}
}
}
}
}
} /**
* 修改数独的值并剔除隐形数字
* @param candi
* @param sudo
* @param row
* @param column
* @param v
*/
private static void modifySudoAndCandi(int[][][] candi, int[][] sudo, int row, int column, int v){
//修改数独的值
sudo[row][column] = v; //剔除备选区数字
candi[row][column] = new int[]{0,0,0,0,0,0,0,0,0};
for (int m=0; m<9; m++){
int[] r = candi[row][m];
r[v-1] = 0;
int[] c = candi[m][column];
c[v-1] = 0;
}
//摒除宫里的唯一性
//取整,获取宫所在数据
int palaceRow = row/3;
int palaceColumn = column/3;
for (int m=0; m<3; m++){
for (int n=0; n<3; n++){
int[] p = candi[palaceRow*3+m][palaceColumn*3+n];
p[v-1] = 0;
}
}
} /**
* 查看行中的隐形数组有没有唯一存在的候选值
* @param candi
* @param sudo
* @return
*/
private static boolean checkRow(int[][][] candi, int[][] sudo){
boolean flag = false;
for (int i=0; i<9; i++){
Map<String ,Set<Integer>> candiMap = new HashMap<>();
int[] row = sudo[i];
for (int j=0; j<9; j++){
if (row[j]==0){
int[] tmp = candi[i][j];
Set<Integer> set = new HashSet<>();
for (int k=0; k<tmp.length; k++){
if (tmp[k]>0) {
set.add(tmp[k]);
}
}
candiMap.put(String.valueOf(i)+"-"+String.valueOf(j), set);
}
}
if (candiMap.size()>0) {
Set<String> keys = candiMap.keySet();
Iterator iterator = keys.iterator();
while (iterator.hasNext()){
String tKey = (String) iterator.next();
//要查看的集合
Set<Integer> set = deepCopySet(candiMap.get(tKey));
//深复制
Set<String> tmpKeys = candiMap.keySet();
Iterator tmpKeyIterator =tmpKeys.iterator();
while (tmpKeyIterator.hasNext()){
String tmpKey = (String) tmpKeyIterator.next();
//取交集
if (!tKey.equals(tmpKey)) {
set.removeAll(candiMap.get(tmpKey));
}
}
//交集取完,集合空了,看下一个结合有没有
if (set.size() == 0){
continue;
}else {
//还剩一个唯一值
if (set.size() == 1){
String[] ks = tKey.split("-");
flag = true;
modifySudoAndCandi(candi, sudo, Integer.parseInt(ks[0]),Integer.parseInt(ks[1]), set.stream().mapToInt(Integer::intValue).toArray()[0] );
}
}
}
}
}
return flag;
} /**
* 查看列中的隐形数组有没有唯一存在的候选值
* @param candi
* @param sudo
* @return
*/
private static boolean checkColumn(int[][][] candi, int[][] sudo){
boolean flag = false;
for (int i=0; i<9; i++){
Map<String ,Set<Integer>> candiMap = new HashMap<>();
for (int j=0; j<9; j++){
if (sudo[j][i]==0){
int[] tmp = candi[j][i];
Set<Integer> set = new HashSet<>();
for (int k=0; k<tmp.length; k++){
if (tmp[k]>0) {
set.add(tmp[k]);
}
}
candiMap.put(String.valueOf(i)+"-"+String.valueOf(j), set);
}
}
if (candiMap.size()>0) {
Set<String> keys = candiMap.keySet();
Iterator iterator = keys.iterator();
while (iterator.hasNext()){
String tKey = (String) iterator.next();
//要查看的集合
Set<Integer> set = deepCopySet(candiMap.get(tKey));
//深复制
Set<String> tmpKeys = candiMap.keySet();
Iterator tmpKeyIterator =tmpKeys.iterator();
while (tmpKeyIterator.hasNext()){
String tmpKey = (String) tmpKeyIterator.next();
//取交集
if (!tKey.equals(tmpKey)) {
set.removeAll(candiMap.get(tmpKey));
}
}
//交集取完,集合空了,看下一个结合有没有
if (set.size() == 0){
continue;
}else {
//还剩一个唯一值
if (set.size() == 1){
String[] ks = tKey.split("-");
flag = true;
modifySudoAndCandi(candi,sudo, Integer.parseInt(ks[1]),Integer.parseInt(ks[0]),set.stream().mapToInt(Integer::intValue).toArray()[0]);
}
}
}
}
}
return flag;
} /**
* 获取隐形数字中宫中两个相等的数字
* @return
*/
private static List<CandiInfo> getEqualCandi(int[][][] candi, int[][] sudo){
//找到两个相等数字
//遍历宫
List<CandiInfo> maps = new ArrayList<>();
for (int m=0; m<3; m++){
for (int n=0; n<3; n++){
Map<String, Set<Integer>> palaceMap = new HashMap<>();
for (int i=0; i<3; i++){
for (int j=0; j<3; j++){
int sudoRow = m*3 + i;
int sudoColumn = n*3 +j;
if (sudo[sudoRow][sudoColumn] == 0) {
int[] tmpX = candi[sudoRow][sudoColumn];
Set<Integer> set = new HashSet<>();
for (int k=0; k<tmpX.length; k++){
if (tmpX[k]>0) {
set.add(tmpX[k]);
}
}
if (set.size() == 2) {
palaceMap.put(String.valueOf(sudoRow) + "-" + String.valueOf(sudoColumn), set);
}
}
}
} Set<String> pSet = palaceMap.keySet();
Iterator pIterator = pSet.iterator();
while (pIterator.hasNext()){
String key = (String) pIterator.next();
Iterator tmpIterator = pSet.iterator();
while (tmpIterator.hasNext()){
String tmpKey = (String) tmpIterator.next();
if (!key.equals(tmpKey)){
Set<Integer> tmpIntSet = palaceMap.get(tmpKey);
Set<Integer> palaceIntSet = deepCopySet(palaceMap.get(key));
palaceIntSet.removeAll(tmpIntSet);
//说明两个集合相等
if (palaceIntSet.size() == 0){
CandiInfo candiInfo = new CandiInfo();
candiInfo.location = key+"|"+tmpKey;
candiInfo.nums = palaceMap.get(key);
maps.add(candiInfo);
}
}
}
}
}
}
List<CandiInfo> infos = new ArrayList<>();
CandiInfo candiInfo = null;
for (CandiInfo info : maps){
if (candiInfo == null){
candiInfo = info;
}else {
if (candiInfo.nums.equals(info.nums)) {
infos.add(info);
}
candiInfo = info;
}
}
return infos;
} /**
* 校验这个数独是不是还满足数独的特点
* 思路:
* 1. 校验行和列有没有重复的数字
* 2. 校验数独是0的格子,对应的隐形数组还有没有值,如果没有候选值,肯定是某一个地方填错了
* @param candi 隐形数组
* @param sudo 数独二维数组
* @return
*/
private static boolean isOkSudo(int[][][] candi, int[][] sudo){
boolean flag = true;
for (int i=0; i<9; i++){
//校验行
Set<Integer> rowSet = new HashSet<>();
//校验列
Set<Integer> clumnSet = new HashSet<>();
for (int j=0; j<9; j++){
int rowV = sudo[i][j];
int cloumV = sudo[j][i];
if (rowV>0){
if (!rowSet.add(rowV)) {
flag = false;
break;
}
}
if (cloumV>0){
if (!clumnSet.add(cloumV)) {
flag = false;
break;
}
} }
if (!flag){
break;
}
}
//校验隐形数字是否为空
for (int m=0; m<9; m++){
for (int n=0; n<9; n++){
if (sudo[m][n] == 0){
int[] s = candi[m][n];
Set<Integer> set = new HashSet<>();
for (int p=0; p<s.length; p++){
if (s[p]>0){
set.add(s[p]);
}
}
if (set.size() == 0){
flag = false;
break;
}
}
}
}
return flag;
} /**
* 深度复制set集合
* @param source
* @return
*/
private static Set<Integer> deepCopySet(Set<Integer> source){
Set<Integer> deepCopy = new HashSet<>();
Iterator iterator = source.iterator();
while (iterator.hasNext()){
deepCopy.add((Integer) iterator.next());
}
return deepCopy;
} public static class CandiInfo{
String location;
Set<Integer> nums;
}
}

以下是解析出的结果:

 5. 经验总结

  从有解析数独这个想法,到代码实现,大约经历了3天左右,在这个过程中会想一下怎么去构造解析,以及代码的逻辑,和解题的思路。对其中的收获就是Set集合的用法,数组的深浅复制,值传递引用传递等,以及怎么去构建一个数据结构来表示想要表达的东西。有些东西确实是了解,但是真正用的时候可能觉得自己知道的还不够,知识需要去积累学习,希望通过一个数独的解题思路,来温故一些基础知识。感谢阅读!

一次Java解析数独的经历的更多相关文章

  1. Java解析OFFICE(word,excel,powerpoint)以及PDF的实现方案及开发中的点滴分享

    Java解析OFFICE(word,excel,powerpoint)以及PDF的实现方案及开发中的点滴分享 在此,先分享下写此文前的经历与感受,我所有的感觉浓缩到一个字,那就是:"坑&qu ...

  2. java解析xml的三种方法

    java解析XML的三种方法 1.SAX事件解析 package com.wzh.sax; import org.xml.sax.Attributes; import org.xml.sax.SAXE ...

  3. atitit.java解析sql语言解析器解释器的实现

    atitit.java解析sql语言解析器解释器的实现 1. 解析sql的本质:实现一个4gl dsl编程语言的编译器 1 2. 解析sql的主要的流程,词法分析,而后进行语法分析,语义分析,构建sq ...

  4. java 解析XML文档

    Java 解析XML文档 一.解析XML文档方式: 1.DOM方式:将整个XML文档读取到内存中,按照XML文件的树状结构图进行解析. 2.SAX方式:基于事件的解析,只需要加载XML中的部分数据,优 ...

  5. Java 解析 XML

    Java 解析 XML 标签: Java基础 XML解析技术有两种 DOM SAX DOM方式 根据XML的层级结构在内存中分配一个树形结构,把XML的标签,属性和文本等元素都封装成树的节点对象 优点 ...

  6. JAVA解析XML的四种方式

    java解析xml文件四种方式 1.介绍 1)DOM(JAXP Crimson解析器) DOM是用与平台和语言无关的方式表示XML文档的官方W3C标准.DOM是以层次结构组织的节点或信息片断的集合.这 ...

  7. JSON 之JAVA 解析

    一.   JSON (JavaScript Object Notation)一种简单的数据格式,比xml更轻巧. Json建构于两种结构:     1.“名称/值”对的集合(A collection ...

  8. Java解析XML汇总(DOM/SAX/JDOM/DOM4j/XPath)

    [目录] 一.[基础知识——扫盲] 二.[DOM.SAX.JDOM.DOM4j简单使用介绍] 三.[性能测试] 四.[对比] 五.[小插曲XPath] 六.[补充] 关键字:Java解析xml.解析x ...

  9. Java 解析epub格式电子书,helloWorld程序,附带源程序和相关jar包

    秀才坤坤出品 一.epub格式电子书 相关材料和源码均在链接中可以下载:http://pan.baidu.com/s/1bnm8YXT 包括 1.JAVA项目工程test_epub,里面包括了jar包 ...

随机推荐

  1. 2019.01.23 ural1519 Formula 1(轮廓线dp)

    传送门 轮廓线dpdpdp模板题. 题意简述:给一个放有障碍的网格图,问有多少种方法能使所有非障碍格子都在同一条哈密顿回路上面. 考虑用括号序列的写法来状压这个轮廓线. 用000表示没有插头,111表 ...

  2. 2018.11.07 hdu1465不容易系列之一(二项式反演)

    传送门 其实标签只是搞笑的. 没那么难. 二项式反演只是杀鸡用牛刀而已. 这道题也只是让你n≤20n\le20n≤20的错排数而已. 还记得那个O(n)O(n)O(n)的递推式吗? 没错那个方法比我今 ...

  3. OSS 视频存储

    我这里加了 封面图片 可以不理睬! 我没有存oss. 阿里的OSS 自己可以去官网下载 我这里放到 Vendor 下的. 1 # 注意这里OSS中 请设置 存储空间名称为公共的 才能直接使用这里返回的 ...

  4. Codeforces Round #523 (Div. 2) F. Katya and Segments Sets (交互题+思维)

    https://codeforces.com/contest/1061/problem/F 题意 假设存在一颗完全k叉树(n<=1e5),允许你进行最多(n*60)次询问,然后输出这棵树的根,每 ...

  5. Python里的拷贝

    可变数据类型:list.dict 不可变数据类型:int.float.string.tuple 引用 https://github.com/taizilongxu/interview_python#4 ...

  6. iconfont阿里巴巴矢量图标库批量保存

    F12输入——var iconList = document.querySelectorAll('.icon-gouwuche1');for (var i = 0; i < iconList.l ...

  7. UVa 11021 Tribles (概率DP + 组合数学)

    题意:有 k 只小鸟,每只都只能活一天,但是每只都可以生出一些新的小鸟,生出 i 个小鸟的概率是 Pi,问你 m 天所有的小鸟都死亡的概率是多少. 析:先考虑只有一只小鸟,dp[i] 表示 i 天全部 ...

  8. Redis的appendfsync参数详解

    redis.conf中的appendfysnc是对redis性能有重要影响的参数之一.可取三种值:always.everysec和no. 设置为always时,会极大消弱Redis的性能,因为这种模式 ...

  9. How to transfer developer profile to one mac to another mac

    Export developer profile from old mac. In the Xcode Organizer, select your team in the Teams section ...

  10. android webview使用心得 屏幕宽度自适应

    webview中右下角的缩放按钮能不能去掉 settings.setDisplayZoomControls(false); //隐藏webview缩放按钮 让Webview加载的页面居中显示有我知道的 ...