HDU6341 2018 Multi-University Training Contest 4 Problem J. Let Sudoku Rotate解题报告(DFS+模拟)

HDU6341  2018 Multi-University Training Contest 4

Problem J. Let Sudoku Rotate

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 530    Accepted Submission(s): 146

Problem Description

Sudoku is a logic-based, combinatorial number-placement puzzle, which is popular around the world.
In this problem, let us focus on puzzles with 16×16 grids, which consist of 4×4 regions. The objective is to fill the whole grid with hexadecimal digits, i.e. 0123456789ABCDEF, so that each column, each row, and each region contains all hexadecimal digits. The figure below shows a solved sudoku.



Yesterday, Kazari solved a sudoku and left it on the desk. However, Minato played a joke with her - he performed the following operation several times.
* Choose a region and rotate it by 90 degrees counterclockwise.
She burst into tears as soon as she found the sudoku was broken because of rotations.
Could you let her know how many operations her brother performed at least?

 

 

Input

The first line of the input contains an integer T (1≤T≤103) denoting the number of test cases.
Each test case consists of exactly 16 lines with 16 characters each, describing a broken sudoku.

 

 

Output

For each test case, print a non-negative integer indicating the minimum possible number of operations.

 

 

Sample Input

1

681D5A0C9FDBB2F7

0A734B62E167D9E5

5C9B73EF3C208410

F24ED18948A5CA63

39FAED5616400B74

D120C4B7CA3DEF38

7EC829A085BE6D51

B56438F129F79C2A

5C7FBC4E3D08719F

AE8B1673BF42A58D

60D3AF25619C30BE

294190D8EA57264C

C7D1B35606835EAB

AF52A1E019BE4306

8B36DC78D425F7C9

E409492FC7FA18D2

 

 

Sample Output

5

 

Hint

 

The original sudoku is same as the example in the statement.

 

题目分析:

这道题我是赛后慢慢补出来的...打比赛的时候太菜了...

这道题给出一个16*16的sudoku,要求经过若干次旋转后变成各行各列都是从0至F的十六进制数,且每个数在各行各列只出现一次。由于每次旋转是旋转其中的一个4*4矩阵。那么可以通过dfs来完成。

具体意思是:深搜每个小sudoku的旋转次数,然后判断当前时刻是否能够满足条件,如果不能则回溯。

代码贴在下面,依次进行分析:

开sudoku[20][20]存放这个数独,然后从(1,1,tmp)位置开始深搜,其中tmp记录旋转的次数。

dfs过程中,我用栈来代替递归。rotate函数与re_rotate函数模拟旋转的方式,草稿纸上画一下就明白了。重点是check步骤,我用了sudoku_row[][]与sudoku_col[][]来记录,第一位表示当前坐标,第二位表示每一个数字,如sudoku_row[3][‘A’]就是第3行旋转到目前状态时A出现的次数,那么如果不重复那么只会是4 8 12 16,重复则会有+1+2的情况。如果没有重复出现的话,那么sudoku_row[x-i][16] == y。

 

最后附上代码:(为了测试方便,我用了一些注释,请无视掉...)

 

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<vector>
#include<cmath>
#include<map>
#include<iostream>
#include<stack>
using namespace std;
int sudoku[20][20];
int newsudoku[20][20];
int sudoku_row[20][20];
int sudoku_col[20][20];
stack<int> q;
string s;
int ans = 0x3f3f3f3f;//这道题一定保证有解存在的 
int MIN(int a,int b)
{
	if(a<b)
		return a;
	return b;
}
void rotate(int rr,int cc,int k)
{
	int r = (rr-1) * 4 + 1;
	int c = (cc-1) * 4 + 1;
	for(int i=0;i<4;++i)
		for(int j=0;j<4;++j)
		{
			if(k == 0)
				newsudoku[r+i][c+j] = sudoku[r+i][c+j];
			else if(k == 1)
				newsudoku[r+i][c+j] = sudoku[3+r-j][c+i];
			else if(k == 2)
				newsudoku[r+i][c+j] = sudoku[3+r-i][3+c-j];
			else if(k == 3)
				newsudoku[r+i][c+j] = sudoku[r+j][3+c-i];
			sudoku_row[r+i][newsudoku[r+i][c+j]]++;
			//cout<<"row=="<<sudoku_row[r+i][newsudoku[r+i][c+j]]<<endl;
			sudoku_row[r+i][16] += sudoku_row[r+i][newsudoku[r+i][c+j]];
			//cout<<"row["<<(r+i)<<"][0]=="<<sudoku_row[r+i][0]<<endl;
			//如果不重复那么只会是4 8 12 16,重复则会有+1+2的情况 
			//cout<<(r+i)<<endl;
			sudoku_col[c+j][newsudoku[r+i][c+j]]++;
			sudoku_col[c+j][16] += sudoku_col[c+j][newsudoku[r+i][c+j]];
		}
}
void re_rotate(int rr,int cc)
{
	int r = (rr - 1) * 4 + 1;
	int c = (cc - 1) * 4 + 1;
	for(int i = 0;i<4;++i)
		for(int j = 0;j<4;++j)
		{
			sudoku_row[r+i][16] -= sudoku_row[r+i][newsudoku[r+i][c+j]];
			sudoku_row[r+i][newsudoku[r+i][c+j]]--;
			sudoku_col[c+j][16] -= sudoku_col[c+j][newsudoku[r+i][c+j]];
			sudoku_col[c+j][newsudoku[r+i][c+j]]--;
			newsudoku[r+i][c+j] = 0;
		}
}
bool check(int row,int col)
{
	int x = row * 4;
	int y = col * 4;
	//cout<<x<<" "<<y<<endl;
	for(int i=0;i<4;++i)
		{
			//cout<<(5+i)<<endl;
			if(sudoku_row[x-i][16] != y)
			{
				//cout<<sudoku_row[x-i][16]<<endl;
				//cout<<6<<epndl;
				return 0;
			}
				
			if(sudoku_col[y-i][16] != x)
			{
//				cout<<sudoku_col[y-i][16]<<endl;
				//cout<<7<<endl;
				return 0;
			}
				
		}
	return 1;
}
void dfs(int row,int col,int tmp)
{
	if(row > 4)
	{
		ans = MIN( ans , tmp );
		//cout<<4<<endl;
		q.pop();
		if(col == 1)
		{
			re_rotate(row-1,4);
			return;		
		}
		else
		{
			re_rotate(row,col-1);		
			return;	
		}
	}
	//cout<<1<<endl;
	for(int k=0;k<4;++k)//4 situations
	{
		//cout<<k<<endl;
		rotate(row,col,k);
		tmp += k;
		q.push(tmp);
		if(check(row,col))//矩阵转到现在还能够成立 
		{
			//cout<<2<<endl;
			if(col+1<=4)
			{
				dfs(row,col+1,tmp);
				tmp -= k;
			}
			else
			{
				dfs(row+1,1,tmp);
				tmp -= k;
			}
		}
		else
		{
			//cout<<3<<endl;
			re_rotate(row,col);
			tmp -= k;
			q.pop();
		}
	}
	//cout<<4<<endl;
	if(row == 1 && col == 1)//over 
		return;
		
	if(col == 1)
		re_rotate(row-1,4);
	else
		re_rotate(row,col-1);
	q.pop();
}
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		memset(sudoku,0,sizeof(sudoku));
		memset(newsudoku,0,sizeof(newsudoku));
		memset(sudoku_row,0,sizeof(sudoku_row));
		memset(sudoku_col,0,sizeof(sudoku_col));
		//cout<<ans<<endl;
		while(!q.empty())
			q.pop();
		
		for(int i=1;i<=16;++i)
		{
			cin>>s;
			//scanf("%s",s);
			for(int j=1;j<=16;++j)
			{
				if(s[j-1] <= '9' && s[j-1]>='0')
					sudoku[i][j] = s[j-1] - '0';
				else
					sudoku[i][j] = s[j-1] - 'A' + 10;
			}
			
		}
		/*for(int i=1;i<=16;++i)
		{
			for(int j=1;j<=16;++j)
				printf("%3d",sudoku[i][j]);
			cout<<endl;
		}*/
		ans = 0x3f3f3f3f;
		dfs(1,1,0);
		printf("%d\n",ans);		
	}
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值