成為數獨高手有哪些好的訓練方法?
除了勤奮之外。
首先平時訓練和比賽是兩回事。
比賽的目的是最快的完成題目,可以用一切非正常手段。比如試數,也就是普通愛好者講的假設法。但實際上標準數獨的邏輯解法里不存在假設的方法。
訓練的目的是有針對性的練習解法,對題目的要求非常高。如果題目質量好有針對性,一天練20道題就足夠,如果題目千篇一律沒有特點,練200道題效果都不會很好。
做題寫小標提示是很有必要的,因為不清楚題目難度,有些難題必須有小標。看世界冠軍在世錦賽決賽中做題也依然是做小標的。當然小標不是候選數全標,而是標註區塊和數對。
標註練好的基礎上再訓練變形規則題目。
更多的訓練、比賽心得可以參考我的博客:http://blog.sina.com.cn/chencensudoku
要好好練的話,初期要學做記號。
每格分成9個小格,對應9個數字。在可能的數字處點上小點。
然後就是消除記號了,這時有很多決竅。就自己網上查一下吧,總之有了記號一切都一目了然,消除的速度特別快。
我印像里最壞的情況,就是碰到很多格子只有兩種可能。這時需要做個假設,然後推出矛盾。這應該是最高級技巧了。
據我所知,很多數獨遊戲軟體都有記號功能。
先帶記號練,這樣對各種情況有個基本了解,熟了再扔掉記號練。
有時碰到很難的數獨,記號還是必需的。
http://www.chessandpoker.com/sudoku-strategy-guide.html
http://www.learn-sudoku.com/pencil-marks.html
但是在我看來這還不全,高級戰術完全沒有。
50毫秒內解決數獨問題
這應該是我想到的解決數獨的最好方法了
首先,你需要做好禿頂,長痘,失眠多夢,看誰都sb,誰看你都覺得你情商低的心理準備
當然你還可能遇上美麗的尿尿聲音很大的女程序猿 @劉贊贊
然後開始學習各類編碼,然後編寫演算法,然後運行,然後看著數獨問題解除
喏,就像這樣去50毫秒解決數獨:
// 優化
int CNumMatrix::Optimize()
{
int x, y;
int i;
bool changed = true;
while(changed)
{
changed = false;
for(x = 0; x &< m_iSize; x++)
{
for(y = 0; y &< m_iSize; y++)
{
if(NumTrans(m_i16mMatrix[x][y]) == 0)
{
return -1;
}
if(m_cmPossible[x][y] == 1)
{
m_cmPossible[x][y] = 0;
for(i = 0; i &< m_iSize; i++)
{
// 減少列可能性
if(m_cmPossible[x][i] &> 0)
{
this-&>RemovePossible(x, i, m_i16mMatrix[x][y]);
if(m_cmPossible[x][i] == 0)
{
return -1;
}
}
// 減少行可能性
if(m_cmPossible[i][y] &> 0)
{
this-&>RemovePossible(i, y, m_i16mMatrix[x][y]);
if(m_cmPossible[i][y] == 0)
{
return -1;
}
}
// 減少區域可能性
if(!RemoveRegionPossible(m_cmRegion[x][y], m_i16mMatrix[x][y]))
{
return -1;
}
}
m_iAssured++;
changed = true;
}
}
}
}
return 0;
}
// 數字翻譯
int CNumMatrix::NumTrans(__int16 mask)
{
mask = ~(0xffff &<&< m_iSize);
if(mask &>= -1 mask &< 3)
{
return mask;
}
switch(mask)
{
case 0x0004:
return 3;
case 0x0008:
return 4;
case 0x0010:
return 5;
case 0x0020:
return 6;
case 0x0040:
return 7;
case 0x0080:
return 8;
case 0x0100:
return 9;
case 0x0200:
return 10;
case 0x0400:
return 11;
case 0x0800:
return 12;
case 0x1000:
return 13;
case 0x2000:
return 14;
case 0x4000:
return 15;
case 0x8000:
return 16;
}
return -1;
}
// 從文件讀取
bool CNumMatrix::ReadFile(LPCTSTR fileName)
{
FILE * fp = fopen(fileName, "r");
if(fp)
{
int x, y;
char stmp[] = " ";
int tmp;
fscanf(fp, "%d %d, %d ", m_iSize, m_ivRegion[0], m_ivRegion[1]);
if(m_iSize &> 0 m_iSize &<= NM_MAX_SIZE Init())
{
for(y = 0; y &< m_iSize; y++)
{
for(x = 0; x &< m_iSize; x++)
{
fscanf(fp, "%c", stmp[0]);
if(stmp[0] == "?")
{
m_i16mMatrix[x][y] = -1;
m_cmPossible[x][y] = m_iSize;
}
else
{
sscanf(stmp, "%X", tmp);
this-&>SetVal(x, y, NM_MAKE_MASK(tmp));
}
fscanf(fp, "%c", stmp[0]);
}
}
}
else
{
fclose(fp);
return false;
}
fclose(fp);
return this-&>CheckPossibility();
}
return false;
}
// 導出到文件
bool CNumMatrix::WriteFile(LPCTSTR fileName)
{
FILE * fp = fopen(fileName, "w");
if(fp)
{
int x, y;
int tmp;
fprintf(fp, "%d %d, %d ", m_iSize, m_ivRegion[0], m_ivRegion[1], m_iAssured);
for(y = 0; y &< m_iSize; y++)
{
for(x = 0; x &< m_iSize - 1; x++)
{
tmp = NumTrans(m_i16mMatrix[x][y]);
if(tmp &>= 0)
{
fprintf(fp, "%X ", tmp);
}
else
{
fprintf(fp, "? ");
}
}
tmp = NumTrans(m_i16mMatrix[x][y]);
if(tmp &>= 0)
{
fprintf(fp, "%X ", tmp);
}
else
{
fprintf(fp, "? ");
}
}
fclose(fp);
return true;
}
return false;
}
// 減少區域可能性
bool CNumMatrix::RemoveRegionPossible(char region, __int16 mask)
{
int x, y;
for(x = 0; x &< m_iSize; x++)
{
for(y = 0; y &< m_iSize; y++)
{
if(m_cmRegion[x][y] == region m_cmPossible[x][y] &> 0)
{
RemovePossible(x, y, mask);
if(m_cmPossible[x][y] == 0)
{
return false;
}
}
}
}
return true;
}
// 可能性檢查
bool CNumMatrix::CheckPossibility()
{
int x, y;
int i;
m_iAssured = 0;
bool zero = false;
for(x = 0; x &< m_iSize; x++)
{
for(y = 0; y &< m_iSize; y++)
{
if(m_cmPossible[x][y] == 0)
{
m_iAssured++;
}
else
{
m_cmPossible[x][y] = m_iSize;
m_i16mMatrix[x][y] = -1;
}
}
}
if(m_iAssured)
{
for(x = 0; x &< m_iSize; x++)
{
for(y = 0; y &< m_iSize; y++)
{
if(m_cmPossible[x][y] == 0)
{
for(i = 0; i &< m_iSize; i++)
{
// 減少列可能性
if(m_cmPossible[x][i] &> 0)
{
this-&>RemovePossible(x, i, m_i16mMatrix[x][y]);
if(m_cmPossible[x][i] == 0)
{
return false;
}
}
// 減少行可能性
if(m_cmPossible[i][y] &> 0)
{
this-&>RemovePossible(i, y, m_i16mMatrix[x][y]);
if(m_cmPossible[i][y] == 0)
{
return false;
}
}
// 減少區域可能性
if(!RemoveRegionPossible(m_cmRegion[x][y], m_i16mMatrix[x][y]))
{
return false;
}
}
}
}
}
}
return true;
}
// 查找解
bool CNumMatrix::FindSolution()
{
static int si = 0;
if(Optimize() &< 0)
{
return false;
}
if(m_iAssured == m_iSize * m_iSize)
{
return true;
}
CNumMatrix tmp;
// 查找最小可能性的格子
int x, y;
int mx = -1, my = -1;
char min = m_iSize + 1;
for(x = 0; x &< m_iSize; x++)
{
for(y = 0; y &< m_iSize; y++)
{
if(m_cmPossible[x][y] != 0 m_cmPossible[x][y] &< min)
{
mx = x;
my = y;
min = m_cmPossible[x][y];
}
}
}
// 取得可能性
__int16 possible[NM_MAX_SIZE];
if(mx == -1)
{
return false;
}
int sum = GetPossibleVal(mx, my, possible);
// 遍歷可能性
int i;
for(i = 0; i &< sum; i++)
{
memcpy(tmp, this, sizeof(CNumMatrix));
tmp.SetVal(mx, my, possible[i]);
if(tmp.CheckPossibility() tmp.FindSolution())
{
memcpy(this, tmp, sizeof(CNumMatrix));
return true;
}
}
return false;
}
微博上喜歡的小姐姐,是個數獨愛好者。
我就抱著打發時間的想法,先去淘寶買了些一共是十本。起初,做初級的時候,一道可以一個小時,還是做不出來的那種,更多的是做到最後發現是錯誤的。因為工作性質的原因,有時候真的超級閑,就一天一天做,錯了拿橡皮擦了又擦。
也許是掌握了些技巧,或者是不想之前急於攻克,慢慢起來,不僅可以做出來,而且會樂於其中。什麼開發左右腦,我都20多歲了,也沒想著開發啥,初意就是看看是什麼。
所以呀,真的沒有什麼特質或者什麼心思,就是簡單的,我現在就在每天堅持做數獨。
之前同事周大牛數獨玩出花的手段就是自己寫了個數獨破解程序,破解數獨經典題庫的速度比之前世界第一的那個程序快一大截。沒記錯的話是12左右個微秒(後面有時間給大家找下機器配置和源碼地址)。
不是我自己的東西,所以還是匿了。
真沒想到數獨還有世錦賽?!
以前玩的時候網上搜到的數獨遊戲技巧。數獨遊戲技巧(圖解)
多做一些特定的解法的題,記住一些常用的辦法,多上論壇跟別人交流,最好就是有人一起做,很好玩的..
多練習就很快了。
我之前有部諾基亞的6300,裡面有個Flash的數獨,每天利用空閑的時候玩會,從easy到hard有200關,我玩到199關。速度越來越快。
自己要有興趣,然後沒事就玩這個,隨時玩。不開心的時候也玩。多了發現自己掌握的技巧會越來越多,拿過來一個數獨,自己就會用最快的速度,將不同的方法一遍遍的過,一個個解決
推薦閱讀:
※一道編程/數學挑戰題,應如何思考?
※網上看到一道數學題,關於dota天梯分的。?
TAG:數學競賽 |