1. 문제

https://www.acmicpc.net/problem/2580

 

2580번: 스도쿠

스도쿠는 18세기 스위스 수학자가 만든 '라틴 사각형'이랑 퍼즐에서 유래한 것으로 현재 많은 인기를 누리고 있다. 이 게임은 아래 그림과 같이 가로, 세로 각각 9개씩 총 81개의 작은 칸으로 이루

www.acmicpc.net

2. 문제의 요구

빈칸을 채워 스도쿠를 완성하라!

 

3. 적용할 알고리즘

백트래킹이 가능하다.

처음에는 빈칸이 10개인 경우 9의 10승으로 시간초과가 날 거라 예상했지만

빈칸을 채울 때마다 조건 확인을 해주면 그 다음 재귀로 넘어가는 경우의 수는 많이 줄어든다.

 

4. 설명

빈칸에 1~9까지 수를 대입해보고

스도쿠 조건에 만족하지 않는다면 그 경우는 패스하고,

만족한다면 다음 빈칸으로 넘어간다.

만약 만족하는 수가 없다면 이전 선택이 잘못된 것이므로

이전의 빈칸으로 돌아간다.

빈칸이 모두 채워졌다면 종료한다.

 

5. 코드

#include<iostream>
#include<vector>
#include<string>
#include<stack>
#include<queue>
#include<tuple>

using namespace std;

const int INF = 0x7f7f7f7f;

typedef long long ll;

int dx[4] = { 1, -1, 0, 0 };
int dy[4] = { 0, 0, 1, -1 };

int board[10][10];

vector<pair<int,int>> zero;

bool Check(int x, int y, int num)
{
	for (int i = 0; i < 9; i++)
	{
		if (board[i][y] == num) return false;
		if (board[x][i] == num) return false;
	}

//주변 9개 칸 중 같은 것이 있는지 확인
//*주의* C언어에서 x / 3 * 3 != x 이다. 
// '/'연산은 나머지를 날려버리기 때문이다.
	for (int i = x / 3 * 3; i < x / 3 * 3 + 3; i++)
	{
		for (int j = y / 3 * 3; j < y / 3 * 3 + 3; j++)
		{
			if (board[i][j] == num) return false;
		}
	}

	return true;
}

int ans[10][10];

void solve(int cur)
{
//빈 칸을 다 채웠을 시 종료
	if (cur == zero.size())
	{
		for (int i = 0; i < 9; i++)
			for (int j = 0; j < 9; j++) ans[i][j] = board[i][j];
		return;
	}

	int cx, cy;
	tie(cx, cy) = zero[cur];

//적절한 수인지 확인 후 다음 빈칸으로 넘어간다
	for (int i = 1; i <= 9; i++)
	{
		if (Check(cx, cy, i))
		{
			board[cx][cy] = i;
			solve(cur + 1);
			board[cx][cy] = 0;
		}
	}
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);

	for (int i = 0; i < 9; i++)
	{
		for (int j = 0; j < 9; j++)
		{
			cin >> board[i][j];
			if (board[i][j] == 0) zero.push_back({ i, j });
		}
	}

	solve(0);

	for (int i = 0; i < 9; i++)
	{
		for (int j = 0; j < 9; j++) cout << ans[i][j] << ' ';
		cout << '\n';
	}
}

6. 느낀 점

그냥 대충 순열, 조합 등으로 계산하고 백트래킹 불가로 생각하지 말고

어떠한 조치를 취하면 백트래킹의 시간복잡도가 대폭 줄어들어

완전탐색이 가능하다는 걸 기억하자

'알고리즘&자료구조 실전문제풀이 > 백트래킹' 카테고리의 다른 글

Boj 1799 비숍  (0) 2023.04.18
boj 16987 계란으로 계란치기  (0) 2023.04.14

1. 문제

https://www.acmicpc.net/problem/1002

 

1002번: 터렛

각 테스트 케이스마다 류재명이 있을 수 있는 위치의 수를 출력한다. 만약 류재명이 있을 수 있는 위치의 개수가 무한대일 경우에는 -1을 출력한다.

www.acmicpc.net

2. 문제에서 원하는 것

A와 B의 좌표가 주어지고 A와 C, B와 C 사이의 거리가

주어질 때 C가 있을 수 있는 위치의 개수는?

3. 적용할 알고리즘

기하, 두 원의 교점

4. 설명

C와의 거리를 이용하면 C가 있을 수 있는 곳을 원으로 표현 가능 하다.

따라서 두 점으로 부터의 거리를 모두 만족하는

두 원의 교점이 C가 있을 수 있는 점이된다.

그리고 두 점 사이의 거리 dis, r1 + r2와 r1 - r2(r1 > r2일 때)의 값을 이용해

교점이 몇 개 생기는지 알 수 있다.

1) dis > r1 + r2

교점이 생기지 않는다.

 

2) dis == r1 + r2

교점이 하나 생긴다(외접)

 

3) dis < r1 + r2

의 경우는 r1 - r2에 따라 또 세 가지로 나뉜다.

 

3-1) dis > r1 - r2

교점이 두개 생긴다

 

3-2) dis == r1 - r2

교점이 하나 생긴다(내접)

 

3-3) dis < r1 - r2

큰 원 안에 작은 원이 있다, 교점이 없다

 

예외) x1 == x2 && y1 == y2 && r1 == r2

두 원이 완전히 겹친다 => 교점이 무한하다.

 

5. 코드

#include<iostream>
#include<vector>
#include<string>
#include<stack>
#include<queue>
#include<tuple>

using namespace std;

const int INF = 0x7f7f7f7f;

typedef long long ll;

int dx[4] = { 1, -1, 0, 0 };
int dy[4] = { 0, 0, 1, -1 };

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);

	int t;
	cin >> t;
	while (t--)
	{
		int x1, y1, r1, x2, y2, r2;
		cin >> x1 >> y1 >> r1 >> x2 >> y2 >> r2;

		int dis = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
		int bigger, smaller;
		if (r1 < r2)
		{
			bigger = r2;
			smaller = r1;
		}
		else
		{
			bigger = r1;
			smaller = r2;
		}

		int sum = bigger + smaller; sum *= sum;
		int sub = bigger - smaller; sub *= sub;

		int ans = 0;

		if (dis > sum)//접하지 않음
			ans = 0;
		else if (dis == sum) //외접한다
			ans = 1;
		else
		{
			if (dis > sub) // 두 점에서 만난다
				ans = 2;
			else if (dis == sub) // 내접한다
				ans = 1;
			else //큰 원 안에 작은 원이 있다
				ans = 0;
		}

		if (r1 == r2 && x1 == x2 && y1 == y2) //똑같다 == 교점이 무한
			ans = -1;

		cout << ans << '\n';
	}
}

6. 느낀 점

이러한 기초적인 수학, 기하학적 논리는 기억해두도록 하자...

 

1. 문제 :https://www.acmicpc.net/problem/16987

 

16987번: 계란으로 계란치기

원래 프로그래머의 기본 소양은 팔굽혀펴기를 단 한 개도 할 수 없는 것이라고 하지만 인범이는 3대 500을 넘기는 몇 안되는 프로그래머 중 한 명이다. 인범이는 BOJ에서 틀린 제출을 할 때마다 턱

www.acmicpc.net

2. 문제에서 원하는 것

계란을 왼쪽에서 오른쪽으로 한 칸씩 움직이며 맨 오른쪽 끝에 있는 계란 까지 손에 들고

다른 임의의 계란을 들어서 부딪혔을 때 계란을 최대 몇 개 깰 수 있는가?

 

3. 적용할 알고리즘

총 계란의 개수가 8개이므로 계란을 들고 어떤 계란과 부딪힐지 모든 경우의 수를

조사하면 총 연산 횟수는 대략 최대 7의 8승(5,764,801)이 되어서

백트래킹을 이용한 완전 탐색이 가능하다.

 

4. 수학적 사고, 새로운 발상

필요 없음, 그냥 단순한 백트래킹

 

5. 코드

#include<iostream>
#include<string>
#include<stack>
#include<tuple>
#include<queue>
#include<algorithm>
#include<bitset>

using namespace std;

int n;

pair<int, int> egg[10];

int ans = 0;

void solve(int cur)
{
    if (cur == n)
    {
        int cnt = 0;
        for (int i = 0; i < n; i++)
        {
            //cout << egg[i].first << ' ';
            if (egg[i].first <= 0) cnt++;
        }
        ans = max(ans, cnt);
        return;
    }

    if (egg[cur].first <= 0)
    {
        solve(cur + 1);
        return;
    }

    for (int i = 0; i < n; i++)
    {
        if (cur == i) continue;
        if (egg[i].first <= 0) continue;

        egg[cur].first -= egg[i].second;
        egg[i].first -= egg[cur].second;
        solve(cur + 1);
        egg[cur].first += egg[i].second;
        egg[i].first += egg[cur].second;
    }

    if (cur == n - 1) solve(cur + 1);
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);

    cin >> n;
    
    for (int i = 0; i < n; i++) cin >> egg[i].first >> egg[i].second;

    solve(0);

    cout << ans;
}

6. 느낀 점

쉬운 문제인데 문제 잘못읽어서 1시간 동안 뻘짓했다ㅋㅋ

앞으로는 문제를 잘 읽고 풀이가 확실히 떠오를 때 구현을 시작하자...

'알고리즘&자료구조 실전문제풀이 > 백트래킹' 카테고리의 다른 글

Boj 1799 비숍  (0) 2023.04.18
Boj 2580 스도쿠  (0) 2023.04.16

+ Recent posts