日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ACwing166数独与183靶形数独

發(fā)布時(shí)間:2024/3/13 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ACwing166数独与183靶形数独 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1.數(shù)獨(dú)題目傳送門:https://www.acwing.com/problem/content/168/
  2.靶形數(shù)獨(dú)題目傳送門:https://www.acwing.com/problem/content/185/
  題目1是一個(gè)普通的數(shù)獨(dú),并且測(cè)試數(shù)據(jù)保證有解,但是測(cè)試數(shù)據(jù)是多組,在搜索上不講技巧的搜索是會(huì)TLE的,就連bitset去處理狀態(tài)都會(huì)。
  題目2是在一個(gè)普通的數(shù)獨(dú)的基礎(chǔ)上加了兩點(diǎn)不同,第一不同是數(shù)據(jù)里有沒有解的情況,第二不同是在九宮格上還有權(quán)值,要在所有方案中尋找最優(yōu)值,即找到一種方案還不行,需要在填滿后,按照規(guī)則每個(gè)格子需要乘上權(quán)值后求和,輸出最大的和值。那么需要把所有方案都找出并維護(hù)最大值。,還好這題數(shù)據(jù)里只有一個(gè)需要解決的數(shù)獨(dú)問題。
  那么把題目1的數(shù)據(jù)求解后,再做第二題就顯得比較容易了。
  那么對(duì)于一個(gè)普通的數(shù)獨(dú)問題,我們采用如何的策略去填數(shù)獨(dú)呢?
  1.找橫、豎和小九宮格中空白處能填的“合法數(shù)字”個(gè)數(shù)最少的格子去填,我想你如果手動(dòng)填數(shù)獨(dú)也會(huì)是這個(gè)策略。如果有最少可以選擇的數(shù)的空白格里可以填的數(shù)有多個(gè),暴力枚舉這幾個(gè)數(shù)搜索回溯,而不是隨意找個(gè)空白格去填。
  2.如何快速確定每個(gè)位置上能填的合法數(shù)字呢,等到要找時(shí),再去一遍掃行,一遍掃列,一遍掃九宮格尋找,顯然速度就會(huì)慢了些。
  在此我們可以運(yùn)用位運(yùn)算來進(jìn)行統(tǒng)計(jì)。具體方法是:

  • 對(duì)于每行、每列、每個(gè)九宮格的3個(gè)二進(jìn)制數(shù)(全局整數(shù)變量)保存哪些數(shù)字可以填。
  • 對(duì)于每個(gè)位置,把它所在行、所在列和所在小九宮格的3個(gè)二進(jìn)制數(shù)進(jìn)行位與運(yùn)算,就可以看到哪些數(shù)字是可以填的。如,假設(shè)(1,1)這個(gè)位置的行上的狀態(tài)是100111000,列上的狀態(tài)是011011011,小九宮格上的狀態(tài)是110111100,那么這個(gè)位上可以用的數(shù)有2個(gè),分別是4,5.因?yàn)槲慌c運(yùn)算后從右往左數(shù)第4個(gè)位上和第5個(gè)位上是1,就表示這兩個(gè)數(shù)沒有出現(xiàn)過。程序?qū)崿F(xiàn)時(shí)可以參考lowbit,取最低位的1采用(x & -x)的方式。
      100111000
      011011011
     &110111100
    --------
      000011000
  • 當(dāng)一個(gè)位置上嘗試放入一個(gè)可以合法的數(shù)后,把該位置的行列和小九宮格的狀態(tài)該位置上的數(shù)置為0,回溯時(shí)改回1即可還原現(xiàn)場(chǎng)。
    題目1具體代碼是:
//同樣的思路用bitset去處理狀態(tài)超時(shí),用位運(yùn)算就AC了,可見bitiset在處理二進(jìn)制數(shù)做 //狀態(tài)時(shí)在速度上稍顯不足。 #include<bits/stdc++.h> using namespace std; int sd[10][10],cnt =0; int row[9],col[9],rcsmall[9]; int num[513],onenum[513]; char c; int rcno[10][10]={{0,0,0,0,0,0,0,0,0,0},{0,1,1,1,2,2,2,3,3,3},{0,1,1,1,2,2,2,3,3,3},{0,1,1,1,2,2,2,3,3,3},{0,4,4,4,5,5,5,6,6,6},{0,4,4,4,5,5,5,6,6,6},{0,4,4,4,5,5,5,6,6,6},{0,7,7,7,8,8,8,9,9,9},{0,7,7,7,8,8,8,9,9,9},{0,7,7,7,8,8,8,9,9,9},}; struct pos{int x,y; }; void print(){for(int i = 1;i<= 9 ;i++){for(int j =1; j<= 9; j++){printf("%d",sd[i][j]);}}printf("\n"); } pos find(){int minn =10;pos temp;temp.x = -1,temp.y = -1;for(int i =1;i<= 9;i++){for(int j = 1;j<= 9; j++){if(sd[i][j]==0){int no = rcno[i][j];int vs = row[i] & col[j] & rcsmall[no]; if(minn >onenum[vs]){minn = onenum[vs];temp.x = i;temp.y = j;}}}}return temp; } bool dfs(int k){if(k == cnt+1){print();return true;}pos p = find(); //找空白位置上可以放置的數(shù)據(jù)選擇性最小的哪個(gè)坐標(biāo)點(diǎn)。 int no = rcno[p.x][p.y];int x = p.x,y = p.y;int vs = row[x] & col[y] & rcsmall[no];//vs存儲(chǔ)的是二進(jìn)制位上是1的對(duì)應(yīng)數(shù)可選。 for( ; vs ; vs = vs - (vs & -vs)){//通過尋找最低位為1的位置,逐漸枚舉每個(gè)可以放入的數(shù)。 int t =num[vs & -vs];row[x] ^= 1<< (t-1);col[y] ^= 1<< (t-1);rcsmall[no] ^= 1<< (t-1); sd[x][y] = t;if(dfs(k+1)) return true;sd[x][y] = 0;row[x] ^= 1<< (t-1);col[y] ^= 1<< (t-1);rcsmall[no] ^= 1<< (t-1); }return false; } void init(){for(int i = 0;i<10;i++){row[i] = (1<<9) - 1;col[i]= (1<<9) - 1;rcsmall[i]= (1<<9) - 1;} } void read(){c = getchar();if(c == 'e') return;sd[1][1] = c =='.' ? 0: c-48;for(int i = 2;i<= 81; i++){c = getchar();sd[(i-1)/9 +1][(i-1) % 9+1] = c =='.' ? 0: c-48;}c = getchar(); //?üê???DD?£ } void pre(){//預(yù)處理原始表中每行每列每個(gè)九宮格中數(shù)字的使用狀態(tài)。 for(int i = 1;i<= 9 ;i++){for(int j =1; j<= 9; j++){int no = rcno[i][j];if(sd[i][j] != 0){row[i] ^= 1<< (sd[i][j]-1);col[j] ^= 1<< (sd[i][j]-1);rcsmall[no] ^= 1<< (sd[i][j]-1); }else{cnt ++;}} } } int main(){ //預(yù)處理每個(gè)數(shù)上對(duì)應(yīng)的二進(jìn)制數(shù)中有幾個(gè)1,用于查找最少合法數(shù)據(jù)的位置時(shí)用。 for(int i =0; i< (1 << 9);i++)for(int j = i; j; j = j - (j & -j))onenum[i] ++;//預(yù)處理數(shù)的二進(jìn)制數(shù)中只有一個(gè)1時(shí),它代表的時(shí)哪個(gè)數(shù)。 for(int i = 1;i<= 9 ;i ++){num[1<< (i-1)] = i;}while(1){cnt = 0;init();read();if(c == 'e')break;pre(); dfs(1);}return 0; }

題目二的題解相比題目1,需要做以下更改:

  • 輸入方式不一樣。
  • 增加一個(gè)權(quán)值數(shù)組。
  • 對(duì)出現(xiàn)填滿時(shí)由輸出改為對(duì)數(shù)值的計(jì)算并維護(hù)。
  • 出現(xiàn)填滿的方案時(shí),需要繼續(xù)尋找下面的其他方案,因此去掉函數(shù)中的返回true,false之類的。
  • 在出現(xiàn)方案時(shí)立個(gè)flag,帶搜索完畢檢查其flag的狀態(tài)來確定是否有解。
    具體代碼如下:
#include<bits/stdc++.h> using namespace std; int sd[10][10],cnt =0; int row[9],col[9],rcsmall[9]; int num[513],onenum[513]; int ans = 0; char c; bool flag = false; int rcno[10][10]={{0,0,0,0,0,0,0,0,0,0},{0,1,1,1,2,2,2,3,3,3},{0,1,1,1,2,2,2,3,3,3},{0,1,1,1,2,2,2,3,3,3},{0,4,4,4,5,5,5,6,6,6},{0,4,4,4,5,5,5,6,6,6},{0,4,4,4,5,5,5,6,6,6},{0,7,7,7,8,8,8,9,9,9},{0,7,7,7,8,8,8,9,9,9},{0,7,7,7,8,8,8,9,9,9},}; int score[10][10]={{0,0,0,0,0,0,0,0,0,0},{0,6,6,6,6,6,6,6,6,6},{0,6,7,7,7,7,7,7,7,6},{0,6,7,8,8,8,8,8,7,6},{0,6,7,8,9,9,9,8,7,6},{0,6,7,8,9,10,9,8,7,6},{0,6,7,8,9,9,9,8,7,6},{0,6,7,8,8,8,8,8,7,6},{0,6,7,7,7,7,7,7,7,6},{0,6,6,6,6,6,6,6,6,6}, }; struct pos{int x,y; }; void getPerfect(){int sum = 0;for(int i = 1;i<= 9 ;i++){for(int j =1; j<= 9; j++){sum += sd[i][j]*score[i][j];}}ans = max(ans,sum); } pos find(){int minn =10;pos temp;temp.x = -1,temp.y = -1;for(int i =1;i<= 9;i++){for(int j = 1;j<= 9; j++){if(sd[i][j]==0){int no = rcno[i][j];int vs = row[i] & col[j] & rcsmall[no]; if(minn >onenum[vs]){minn = onenum[vs];temp.x = i;temp.y = j;}}}}return temp; } void dfs(int k){if(k == cnt+1){flag = true;getPerfect();return;}pos p = find(); //找空白位置上可以放置的數(shù)據(jù)選擇性最小的哪個(gè)坐標(biāo)點(diǎn)。 int no = rcno[p.x][p.y];int x = p.x,y = p.y;int vs = row[x] & col[y] & rcsmall[no];//vs存儲(chǔ)的是二進(jìn)制位上是1的對(duì)應(yīng)數(shù)可選。 for( ; vs ; vs = vs - (vs & -vs)){//通過尋找最低位為1的位置,逐漸枚舉每個(gè)可以放入的數(shù)。 int t =num[vs & -vs];row[x] ^= 1<< (t-1);col[y] ^= 1<< (t-1);rcsmall[no] ^= 1<< (t-1); sd[x][y] = t;dfs(k+1);sd[x][y] = 0;row[x] ^= 1<< (t-1);col[y] ^= 1<< (t-1);rcsmall[no] ^= 1<< (t-1); } } void init(){for(int i = 0;i<10;i++){row[i] = (1<<9) - 1;col[i]= (1<<9) - 1;rcsmall[i]= (1<<9) - 1;} } void read(){for(int i = 1;i<= 9;i++){for(int j = 1;j<= 9 ;j++){scanf("%d",&sd[i][j]);}} } void pre(){for(int i = 1;i<= 9 ;i++){for(int j =1; j<= 9; j++){int no = rcno[i][j];if(sd[i][j] != 0){row[i] ^= 1<< (sd[i][j]-1);col[j] ^= 1<< (sd[i][j]-1);rcsmall[no] ^= 1<< (sd[i][j]-1); }else{cnt ++;}} } } int main(){ for(int i =0; i< (1 << 9);i++)for(int j = i; j; j = j - (j & -j))onenum[i] ++;for(int i = 1;i<= 9 ;i ++){num[1<< (i-1)] = i;} cnt = 0;init();read();pre(); dfs(1);if(flag)cout << ans;else cout << "-1";return 0; }

好吧,終于還是咬咬牙把前幾天一口氣用bitset+搜索的方式寫完且TLE程序給改AC了,也終于很快完成了靶形數(shù)獨(dú),在幾年前不敢寫的程序現(xiàn)在看來也不過如此,確實(shí)成長了,但是速度嘛不言而喻,就像我跑馬拉松,人家早就在4小時(shí)內(nèi)到達(dá)了終點(diǎn),我設(shè)定的目標(biāo)是6小時(shí)內(nèi)完賽,享受沿途風(fēng)景,感受途中心情變化,沒人催,就靠點(diǎn)自律,自己感動(dòng)自己,跑起來自然那就叫一個(gè)慢!

總結(jié)

以上是生活随笔為你收集整理的ACwing166数独与183靶形数独的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。