알고리즘/백준

백준 1021번 - 회전하는 큐 / Go

Hwisaek 2022. 4. 17. 16:28
반응형

문제: https://www.acmicpc.net/problem/1021

 

1021번: 회전하는 큐

첫째 줄에 큐의 크기 N과 뽑아내려고 하는 수의 개수 M이 주어진다. N은 50보다 작거나 같은 자연수이고, M은 N보다 작거나 같은 자연수이다. 둘째 줄에는 지민이가 뽑아내려고 하는 수의 위치가

www.acmicpc.net

문제 설명

더보기

문제

지민이는 N개의 원소를 포함하고 있는 양방향 순환 큐를 가지고 있다. 지민이는 이 큐에서 몇 개의 원소를 뽑아내려고 한다.

지민이는 이 큐에서 다음과 같은 3가지 연산을 수행할 수 있다.

  1. 첫 번째 원소를 뽑아낸다. 이 연산을 수행하면, 원래 큐의 원소가 a1, ..., ak이었던 것이 a2, ..., ak와 같이 된다.
  2. 왼쪽으로 한 칸 이동시킨다. 이 연산을 수행하면, a1, ..., ak가 a2, ..., ak, a1이 된다.
  3. 오른쪽으로 한 칸 이동시킨다. 이 연산을 수행하면, a1, ..., ak가 ak, a1, ..., ak-1이 된다.

큐에 처음에 포함되어 있던 수 N이 주어진다. 그리고 지민이가 뽑아내려고 하는 원소의 위치가 주어진다. (이 위치는 가장 처음 큐에서의 위치이다.) 이때, 그 원소를 주어진 순서대로 뽑아내는데 드는 2번, 3번 연산의 최솟값을 출력하는 프로그램을 작성하시오.

입력

첫째 줄에 큐의 크기 N과 뽑아내려고 하는 수의 개수 M이 주어진다. N은 50보다 작거나 같은 자연수이고, M은 N보다 작거나 같은 자연수이다. 둘째 줄에는 지민이가 뽑아내려고 하는 수의 위치가 순서대로 주어진다. 위치는 1보다 크거나 같고, N보다 작거나 같은 자연수이다.

출력

첫째 줄에 문제의 정답을 출력한다.

예제 입력 1 복사

10 3
1 2 3

예제 출력 1 복사

0

예제 입력 2 복사

10 3
2 9 5

예제 출력 2 복사

8

예제 입력 3 복사

32 6
27 16 30 11 6 23

예제 출력 3 복사

59

예제 입력 4 복사

10 10
1 6 3 2 7 9 8 4 10 5

예제 출력 4 복사

14
​

출처

  • 문제를 번역한 사람: baekjoon
  • 문제의 오타를 찾은 사람: dhdh6190
  • 데이터를 추가한 사람: djm03178

알고리즘 분류


 


정답

package main

import (
	"bufio"
	"math"
	"os"
	"strconv"
	"strings"
	"testing"
)

func main() {
	rd := bufio.NewReader(os.Stdin)
	wr := bufio.NewWriter(os.Stdout)

	strArr := strings.Split(scan(rd), " ")
	n, _ := strconv.Atoi(strArr[0])
	m, _ := strconv.Atoi(strArr[1])

	target := []int{}
	for _, s := range strings.Split(scan(rd), " ") {
		atoi, _ := strconv.Atoi(s)
		target = append(target, atoi)
	}

	_, _ = wr.WriteString(strconv.Itoa(solution1021(n, m, target)))
	_ = wr.Flush()
}

func solution1021(n, m int, target []int) (count int) {
	arr := make([]int, n)
	for i := 0; i < n; i++ {
		arr[i] = i + 1
	}

	size := len(target)
	for size > 0 {
		idx := getIndexOf(&arr, &target)

		sizeArr := len(arr)
		if idx < int(math.Ceil(float64(sizeArr)/2)) {
			arr = append(arr[idx+1:], arr[:idx]...)
			count += idx
		} else {
			arr = append(arr[idx+1:], arr[:idx]...)
			count += sizeArr - idx
		}

		size = len(target)
	}
	return
}

func getIndexOf(arr *[]int, target *[]int) int {
	for i, i2 := range *arr {
		if i2 == (*target)[0] {
			*target = (*target)[1:]
			return i
		}
	}
	return -1
}

func scan(rd *bufio.Reader) string {
	str, _ := rd.ReadString('\n') // 여기서 text는 마지막에 줄바꿈 문자를 포함하므로
	str = strings.TrimSpace(str)  // 줄바꿈 문자를 제거해야 함
	return str
}

func Benchmark1021(b *testing.B) {
	target := make([]int, 1000)
	for i := 0; i < 1000; i++ {
		target[i] = i + 1
	}
	for i := 1; i < b.N; i++ {
		solution1021(1000, 1000, target)
	}
}

func Test_solution1021(t *testing.T) {
	type args struct {
		n      int
		m      int
		target []int
	}
	tests := []struct {
		name       string
		args       args
		wantResult int
	}{
		{
			name: "",
			args: args{
				n:      10,
				m:      3,
				target: []int{1, 2, 3},
			},
			wantResult: 0,
		},
		{
			name: "",
			args: args{
				n:      10,
				m:      3,
				target: []int{2, 9, 5},
			},
			wantResult: 8,
		},
		{
			name: "",
			args: args{
				n:      32,
				m:      6,
				target: []int{27, 16, 30, 11, 6, 23},
			},
			wantResult: 59,
		},
		{
			name: "",
			args: args{
				n:      10,
				m:      10,
				target: []int{1, 6, 3, 2, 7, 9, 8, 4, 10, 5},
			},
			wantResult: 14,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if gotResult := solution1021(tt.args.n, tt.args.m, tt.args.target); gotResult != tt.wantResult {
				t.Errorf("solution1021() = %v, want %v", gotResult, tt.wantResult)
			}
		})
	}
}

 


풀이

 큐를 최소한으로 움직이면서 원하는 숫자를 뽑아내는 문제이다. 파이썬으로 진행했으면 얼마나 간단히 풀 수 있었을까 새삼 느껴지는 문제였다.

 

이 문제는 다음과 같은 방법으로 풀었다.

  1. 숫자를 뽑아 낼 배열(이하 arr)을 만들어 1부터 N까지 순서대로 넣는다.
  2. 뽑아낼 원소의 인덱스를 찾는다
  3. 인덱스가  arr의 길이를 올림한 값의 절반 이하면 인덱스만큼 횟수를 더한다. 
  4. 그렇지 않으면 arr의 길이에서 인덱스를 뺀 만큼 횟수를 더한다.

이 문제를 풀면서 파이썬의 편리함을 느끼는 한편, 편리함을 찾느라 실력이 떨어졌다는게 느껴지는 문제였다.

반응형