wikioi p1174 靶形數獨
阿新 • • 發佈:2019-01-25
這題我原先是下標程的,先貼標程程式碼,待會我再貼我自己的程式碼。
pascal 不喜勿噴~
type ctype=array[1..9,1..9,1..9]of boolean; nctype=array[1..9,1..9]of longint; const w:array[1..9,1..9]of integer=((6,6,6,6,6 ,6,6,6,6), (6,7,7,7,7 ,7,7,7,6), (6,7,8,8,8 ,8,8,7,6), (6,7,8,9,9 ,9,8,7,6), (6,7,8,9,10,9,8,7,6), (6,7,8,9,9 ,9,8,7,6), (6,7,8,8,8 ,8,8,7,6), (6,7,7,7,7 ,7,7,7,6), (6,6,6,6,6 ,6,6,6,6)); vb:array[1..9,1..9]of integer=((1,1,1,2,2,2,3,3,3), (1,1,1,2,2,2,3,3,3), (1,1,1,2,2,2,3,3,3), (4,4,4,5,5,5,6,6,6), (4,4,4,5,5,5,6,6,6), (4,4,4,5,5,5,6,6,6), (7,7,7,8,8,8,9,9,9), (7,7,7,8,8,8,9,9,9), (7,7,7,8,8,8,9,9,9)); var v:array[1..82]of boolean; k:array[0..82,1..2]of longint; c:ctype;nc:nctype; depth,ans:longint; flag:boolean; procedure init; var i,j,x,m,n:longint; begin depth:=0;ans:=0;flag:=false; for i:=1 to 9 do for j:=1 to 9 do nc[i,j]:=9; fillchar(c,sizeof(c),true);fillchar(v,sizeof(v),false); for i:=1 to 9 do begin for j:=1 to 9 do begin read(x); if x>0 then begin ans:=ans+x*w[i,j]; nc[i,j]:=0; for m:=1 to 9 do for n:=1 to 9 do begin if nc[m,n]=0 then continue; if (c[m,n,x])and((m=i)or(n=j)or(vb[i,j]=vb[m,n])) then begin c[m,n,x]:=false;dec(nc[m,n]);end; end; end else begin inc(depth);k[depth,1]:=i;k[depth,2]:=j;end; end; readln; end; end; procedure outit; begin if flag then writeln(ans) else writeln('-1'); end; procedure dfs(d,max:longint;c:ctype;nc:nctype); var i,j,min,m,x,y:longint;c1:ctype;nc1:nctype; begin if d=depth+1 then begin if max>ans then ans:=max;flag:=true;exit; end; min:=10; for i:=1 to depth do if not (v[i])and(nc[k[i,1],k[i,2]]<min) then begin min:=nc[k[i,1],k[i,2]];m:=i;end; x:=k[m,1];y:=k[m,2];v[m]:=true; for i:=1 to 9 do if c[x,y,i] then begin c1:=c;nc1:=nc; for j:=1 to depth do begin if (not v[j])and c1[k[j,1],k[j,2],i]and((k[j,1]=x)or(k[j,2]=y)or(vb[k[j,1],k[j,2]]=vb[x,y])) then begin c1[k[j,1],k[j,2],i]:=false;dec(nc1[k[j,1],k[j,2]]);end; if nc1[k[j,1],k[j,2]]=0 then break; end; dfs(d+1,max+i*w[x,y],c1,nc1); end; v[m]:=false; end; begin {(input,'d:in.txt');(input);(,'d:out.txt');(output);} init; dfs(1,ans,c,nc); outit; {close(input);close(output);} end.
然後我們就可以思考一個問題。數獨的規則。
每行 每列 每個小數獨都不能有相同的數字。
所以我們分別用Line list和sud記錄重複。
這樣搜尋效率會增加。
但是bfs存在一個問題。就是隻能得40分。
因為會超空間。
#include<stdio.h> #include<math.h> #include<stdlib.h> #include<algorithm> #include<queue> #include<iostream> #include<memory.h> using namespace std; const int MAX_N = 9; const int MAX_M = 10; int score[MAX_N][MAX_N]={ {6,6,6,6,6,6,6,6,6}, {6,7,7,7,7,7,7,7,6}, {6,7,8,8,8,8,8,7,6}, {6,7,8,9,9,9,8,7,6}, {6,7,8,9,10,9,8,7,6}, {6,7,8,9,9,9,8,7,6}, {6,7,8,8,8,8,8,7,6}, {6,7,7,7,7,7,7,7,6}, {6,6,6,6,6,6,6,6,6}, }; //靶型得分 int ans = -1; //表示答案 struct node { int sudoku[MAX_M][MAX_N]; bool line[MAX_M][MAX_M]; //行 bool list[MAX_M][MAX_M]; //列 bool sud[MAX_M][MAX_M]; //數獨 int x,y; }head,next; //靶型數獨 queue<struct node> Q; int solve_score(struct node R) //計算總得分 { int i,j; int tmp = 0; for (i=0;i<MAX_N;i++) for (j=0;j<MAX_N;j++) tmp+=score[i][j]*R.sudoku[i][j]; return tmp ; } int which(int x,int y) //尋找該單元格在第幾個小數獨中 { if (x>=0&&x<=2) { if (y>=0&&y<=2) return 1; if (y>=3&&y<=5) return 2; if (y>=6&&y<=8) return 3; } if (x>=3&&x<=5) { if (y>=0&&y<=2) return 4; if (y>=3&&y<=5) return 5; if (y>=6&&y<=8) return 6; } if (x>=6&&x<=8) { if (y>=0&&y<=2) return 7; if (y>=3&&y<=5) return 8; if (y>=6&&y<=8) return 9; } } int putG(struct node R) { int i,j; for (i=0;i<MAX_N;i++) { for (j=0;j<MAX_N;j++) printf("%d ",R.sudoku[i][j]); printf("\n"); } printf("\n"); } int init() //初始化 { memset(head.line,false,sizeof(head.line)); memset(head.list,false,sizeof(head.list)); memset(head.sud,false,sizeof(head.sud)); int i ,j ; for (i=0;i<MAX_N;i++) for (j=0;j<MAX_N;j++) scanf("%d",&head.sudoku[i][j]); for (i=0;i<MAX_N;i++) for (j=0;j<MAX_N;j++) if (head.sudoku[i][j]) { head.line[i][head.sudoku[i][j]]=true; head.list[j][head.sudoku[i][j]]=true; head.sud[which(i,j)][head.sudoku[i][j]]=true; } head.x = head.y=0; Q.push(head); } bool Cash(struct node R,int x,int y) //檢查是否發生衝突 { int i=R.sudoku[x][y]; if (R.line[x][i]) return false; if (R.list[y][i]) return false; if (R.sud[which(x,y)][i]) return false; return true; } int work() { int i; int x,y; int nx,ny; while(!Q.empty()) { head=Q.front(); Q.pop(); x=head.x; y=head.y; //putG(head); //for (int v=1;v<=50000000;v++); if (x==MAX_N) { ans=max(solve_score(head),ans); continue; } if (y==MAX_N-1) nx=x+1,ny=0; else nx=x,ny=y+1; if (head.sudoku[x][y]) { next=head; next.x=nx; next.y=ny; Q.push(next); } else for (i=1;i<=MAX_N;i++) { next=head; next.x=nx; next.y=ny; next.sudoku[x][y]=i; if (Cash(next,x,y)) { next.line[x][i] = true; next.list[y][i] = true; next.sud[which(x,y)][i] =true; Q.push(next); } } } } int put() { printf("%d",ans); } int main() { init(); work(); //putG(head); put(); //while(1); return 0; }
然後我們可以將他改成dfs
這樣就不會超空間了。
不過任然存在一個問題,就是超時。
#include<stdio.h> #include<math.h> #include<stdlib.h> #include<algorithm> #include<iostream> #include<memory.h> using namespace std; const int MAX_N = 9; const int MAX_M = 10; int score[MAX_N][MAX_N]={ {6,6,6,6,6,6,6,6,6}, {6,7,7,7,7,7,7,7,6}, {6,7,8,8,8,8,8,7,6}, {6,7,8,9,9,9,8,7,6}, {6,7,8,9,10,9,8,7,6}, {6,7,8,9,9,9,8,7,6}, {6,7,8,8,8,8,8,7,6}, {6,7,7,7,7,7,7,7,6}, {6,6,6,6,6,6,6,6,6}, }; //靶型得分 int ans = -1; //表示答案 int sudoku[MAX_M][MAX_N];//靶型數獨 bool line[MAX_M][MAX_M]; //行 bool list[MAX_M][MAX_M]; //列 bool sud[MAX_M][MAX_M]; //數獨 int which(int x,int y) //尋找該單元格在第幾個小數獨中 { if (x>=0&&x<=2) { if (y>=0&&y<=2) return 1; if (y>=3&&y<=5) return 2; if (y>=6&&y<=8) return 3; } if (x>=3&&x<=5) { if (y>=0&&y<=2) return 4; if (y>=3&&y<=5) return 5; if (y>=6&&y<=8) return 6; } if (x>=6&&x<=8) { if (y>=0&&y<=2) return 7; if (y>=3&&y<=5) return 8; if (y>=6&&y<=8) return 9; } } int init() //初始化 { memset(line,false,sizeof(line)); memset(list,false,sizeof(list)); memset(sud,false,sizeof(sud)); int i ,j ; for (i=0;i<MAX_N;i++) for (j=0;j<MAX_N;j++) scanf("%d",&sudoku[i][j]); for (i=0;i<MAX_N;i++) for (j=0;j<MAX_N;j++) if (sudoku[i][j]) { line[i][sudoku[i][j]]=true; list[j][sudoku[i][j]]=true; sud[which(i,j)][sudoku[i][j]]=true; } } bool Cash(int x,int y,int i) //檢查是否發生衝突 { if (line[x][i]) return false; if (list[y][i]) return false; if (sud[which(x,y)][i]) return false; return true; } int dfs(int x,int y,int tmp) { if (x==MAX_N) { ans=max(tmp,ans); return 0; } int i; int nx,ny; if (y==MAX_N-1) nx=x+1,ny=0; else nx=x,ny=y+1; if (sudoku[x][y]) dfs(nx,ny,tmp+sudoku[x][y]*score[x][y]); else for (i=1;i<=MAX_N;i++) if (Cash(x,y,i)) { sudoku[x][y]=i; line[x][i] = true; list[y][i] = true; sud[which(x,y)][i] = true; dfs(nx,ny,tmp+i*score[x][y]); line[x][i] = false; list[y][i] = false; sud[which(x,y)][i] = false; sudoku[x][y]=0;//這句話必不可少 } } int put() { printf("%d",ans); } int main() { init(); dfs(0,0,0); put(); return 0; }
根據啟發式搜尋的思想,運用估價函式可以通過改變搜尋的順序增加速度。
但是這題資料比較奇葩,如果倒過來搜,也就是從最後一列最後一行開始搜尋的話速度會快很多。
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<algorithm>
#include<iostream>
#include<memory.h>
using namespace std;
const int MAX_N = 9;
const int MAX_M = 10;
int score[MAX_N][MAX_N]={
{6,6,6,6,6,6,6,6,6},
{6,7,7,7,7,7,7,7,6},
{6,7,8,8,8,8,8,7,6},
{6,7,8,9,9,9,8,7,6},
{6,7,8,9,10,9,8,7,6},
{6,7,8,9,9,9,8,7,6},
{6,7,8,8,8,8,8,7,6},
{6,7,7,7,7,7,7,7,6},
{6,6,6,6,6,6,6,6,6},
}; //靶型得分
int ans = -1; //表示答案
int sudoku[MAX_M][MAX_N];//靶型數獨
bool line[MAX_M][MAX_M]; //行
bool list[MAX_M][MAX_M]; //列
bool sud[MAX_M][MAX_M]; //數獨
int which(int x,int y) //尋找該單元格在第幾個小數獨中
{
if (x>=0&&x<=2)
{
if (y>=0&&y<=2) return 1;
if (y>=3&&y<=5) return 2;
if (y>=6&&y<=8) return 3;
}
if (x>=3&&x<=5)
{
if (y>=0&&y<=2) return 4;
if (y>=3&&y<=5) return 5;
if (y>=6&&y<=8) return 6;
}
if (x>=6&&x<=8)
{
if (y>=0&&y<=2) return 7;
if (y>=3&&y<=5) return 8;
if (y>=6&&y<=8) return 9;
}
}
int init() //初始化
{
memset(line,false,sizeof(line));
memset(list,false,sizeof(list));
memset(sud,false,sizeof(sud));
int i ,j ;
for (i=0;i<MAX_N;i++)
for (j=0;j<MAX_N;j++)
scanf("%d",&sudoku[i][j]);
for (i=0;i<MAX_N;i++)
for (j=0;j<MAX_N;j++)
if (sudoku[i][j])
{
line[i][sudoku[i][j]]=true;
list[j][sudoku[i][j]]=true;
sud[which(i,j)][sudoku[i][j]]=true;
}
}
bool Cash(int x,int y,int i) //檢查是否發生衝突
{
if (line[x][i]) return false;
if (list[y][i]) return false;
if (sud[which(x,y)][i]) return false;
return true;
}
int dfs(int x,int y,int tmp)
{
if (x==MAX_N)
{
ans=max(tmp,ans);
return 0;
}
int i;
int nx,ny;
if (y==MAX_N-1) nx=x+1,ny=0;
else nx=x,ny=y+1;
if (sudoku[x][y])
dfs(nx,ny,tmp+sudoku[x][y]*score[x][y]);
else
for (i=1;i<=MAX_N;i++)
if (Cash(x,y,i))
{
sudoku[x][y]=i;
line[x][i] = true;
list[y][i] = true;
sud[which(x,y)][i] = true;
dfs(nx,ny,tmp+i*score[x][y]);
line[x][i] = false;
list[y][i] = false;
sud[which(x,y)][i] = false;
sudoku[x][y]=0;//這句話必不可少
}
}
int put()
{
printf("%d",ans);
}
int main()
{
init();
dfs(0,0,0);
put();
return 0;
}
另外,網上說dl的速度非常快。但是不會寫啊。
啟發式搜尋其實還是不難的。
但是估價函式的設計還比較麻煩。