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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章