wikioi p1174 靶形数独

这题我原先是下标程的,先贴标程代码,待会我再贴我自己的代码。

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的速度非常快。但是不会写啊。

启发式搜索其实还是不难的。

但是估价函数的设计还比较麻烦。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值