这题我原先是下标程的,先贴标程代码,待会我再贴我自己的代码。
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的速度非常快。但是不会写啊。
启发式搜索其实还是不难的。
但是估价函数的设计还比较麻烦。