문제

두 개의 자연수를 입력받아 최대 공약수와 최소 공배수를 출력하는 프로그램을 작성하시오.

 

입력

첫째 줄에는 두 개의 자연수가 주어진다. 이 둘은 10,000이하의 자연수이며 사이에 한 칸의 공백이 주어진다.

출력

첫째 줄에는 입력으로 주어진 두 수의 최대공약수를, 둘째 줄에는 입력으로 주어진 두 수의 최소 공배수를 출력한다.

 

방법 1. 1씩 증가/감소하면서 최소공배수/최대공약수 찾기

#include <iostream>

using namespace std;

int main()
{
    int a,b, s1, s2;
    cin >> a >> b;
    if(a>b){
        s1 = b;
        s2 = a;
    }else{
        s1 = a;
        s2 = b;
    }
    for(int i=s1; i>0; i--){
        if(a%i==0 && b%i==0){
            printf("%d\n", i);
            break;
        }
    }
    for(int i=s2; i<987654321; i++){
        if(i%a==0 && i%b==0){
            printf("%d\n", i);
            break;
        }
    }

    return 0;
}

 

 

방법 2. 유클리드 호제법을 이용하여 최대공약수/최소공배수 찾기

#include <iostream>

using namespace std;

int get_gcd(int a, int b){
    int mod;
    while(b){
        mod = a % b;
        a = b;
        b = mod;
    }
    return a;
}

int get_lcm(int a, int b, int gcd){
    return a * b / gcd;
}

int main()
{
    int a,b,gcd,lcm;
    cin >> a >> b;
    gcd = get_gcd(a,b);
    lcm = get_lcm(a,b,gcd);
    printf("%d\n%d",gcd, lcm);
    return 0;
}

'알고리즘 문제풀이' 카테고리의 다른 글

[백준] 10816 숫자 카드 2 c++  (0) 2022.02.08
[백준] 9012번 괄호 c++  (0) 2022.02.06
[백준] 1920번 수 찾기 c++  (0) 2022.02.04
[백준] 1259번 팰린드롬수 c++  (0) 2022.02.04
[백준] 2475번 검증수 c++  (0) 2022.02.04

문제

 

N개의 정수 A[1], A[2], …, A[N]이 주어져 있을 때, 이 안에 X라는 정수가 존재하는지 알아내는 프로그램을 작성하시오.

입력

첫째 줄에 자연수 N(1 ≤ N ≤ 100,000)이 주어진다. 다음 줄에는 N개의 정수 A[1], A[2], …, A[N]이 주어진다. 다음 줄에는 M(1 ≤ M ≤ 100,000)이 주어진다. 다음 줄에는 M개의 수들이 주어지는데, 이 수들이 A안에 존재하는지 알아내면 된다. 모든 정수의 범위는 -231 보다 크거나 같고 231보다 작다.

출력

M개의 줄에 답을 출력한다. 존재하면 1을, 존재하지 않으면 0을 출력한다.

 

문제접근 및 풀이

1. 배열 A를 오름차순으로 sort 한 후

2. M개의 각 정수에 대해 A배열에 존재하는 지 여부를 이분탐색을 이용해 찾는 방식이다.

1번에서 정렬 시 nlogn, 2번 탐색시 m개의 원소에 대해 이분탐색(logm)이 사용된다.

 

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int n,m;
vector<int> a,b;

int find(int key){
    int mid=0;
    int start = 0;
    int end = a.size()-1;
    
    while(end - start >= 0){
        mid = (start+end) / 2;
        if(key == a[mid]) return 1;
        if(key > a[mid]) start = mid + 1;
        if(key < a[mid]) end = mid - 1;
    }
    return 0;
}

int main()
{
    
    scanf("%d", &n);
    for(int x,i=0; i<n; i++){
        scanf("%d", &x);
        a.push_back(x);
    }
    scanf("%d", &m);
    for(int x,i=0; i<m; i++){
        scanf("%d", &x);
        b.push_back(x);
    }
    
    sort(a.begin(), a.end());
    
    for(int i=0; i<b.size(); i++){
        if(find(b[i])){
            printf("1\n");
        }else{
            printf("0\n");
        }
    }
    

    return 0;
}

 

문제

어떤 단어를 뒤에서부터 읽어도 똑같다면 그 단어를 팰린드롬이라고 한다. 'radar', 'sees'는 팰린드롬이다.

수도 팰린드롬으로 취급할 수 있다. 수의 숫자들을 뒤에서부터 읽어도 같다면 그 수는 팰린드롬수다. 121, 12421 등은 팰린드롬수다. 123, 1231은 뒤에서부터 읽으면 다르므로 팰린드롬수가 아니다. 또한 10도 팰린드롬수가 아닌데, 앞에 무의미한 0이 올 수 있다면 010이 되어 팰린드롬수로 취급할 수도 있지만, 특별히 이번 문제에서는 무의미한 0이 앞에 올 수 없다고 하자.

입력

입력은 여러 개의 테스트 케이스로 이루어져 있으며, 각 줄마다 1 이상 99999 이하의 정수가 주어진다. 입력의 마지막 줄에는 0이 주어지며, 이 줄은 문제에 포함되지 않는다.

출력

각 줄마다 주어진 수가 팰린드롬수면 'yes', 아니면 'no'를 출력한다.

 

#include <iostream>
#include <cstring>

using namespace std;

int main()
{
    while(1){
        string a;
        bool pass= true;
        cin >> a;
        if(a == "0") return 0;
        for(int i=0; i<a.length()/2; i++){
            if(a[i] != a[a.length()-1-i]){
                pass = false;
            }
        }
        if(pass) printf("yes\n");
        else printf("no\n");
    }
    
    
    return 0;
}

 

문제

컴퓨터를 제조하는 회사인 KOI 전자에서는 제조하는 컴퓨터마다 6자리의 고유번호를 매긴다. 고유번호의 처음 5자리에는 00000부터 99999까지의 수 중 하나가 주어지며 6번째 자리에는 검증수가 들어간다. 검증수는 고유번호의 처음 5자리에 들어가는 5개의 숫자를 각각 제곱한 수의 합을 10으로 나눈 나머지이다.

예를 들어 고유번호의 처음 5자리의 숫자들이 04256이면, 각 숫자를 제곱한 수들의 합 0+16+4+25+36 = 81 을 10으로 나눈 나머지인 1이 검증수이다.

입력

첫째 줄에 고유번호의 처음 5자리의 숫자들이 빈칸을 사이에 두고 하나씩 주어진다.

출력

첫째 줄에 검증수를 출력한다.

 

#include <iostream>

using namespace std;

int main()
{
    int a[5], res=0;
    for(int i=0; i<5; i++){
        scanf("%d", &a[i]);
    }
    for(int i=0; i<5; i++){
        res += a[i]*a[i];
    }
    cout << res%10;
    return 0;
}

'알고리즘 문제풀이' 카테고리의 다른 글

[백준] 1920번 수 찾기 c++  (0) 2022.02.04
[백준] 1259번 팰린드롬수 c++  (0) 2022.02.04
[백준] 1074번 Z c++  (0) 2022.01.25
[백준] 2146번 다리 만들기 c++  (0) 2021.09.05
[백준] 15663 N과 M (9) c++  (0) 2021.09.04

1부 배움이란 무엇인가?

 

  • 배움의 7가지 정의

- 가장 일반적인 배움의 정의: 배움이란 외부세계에 대한 내부모델을 만드는 것이다.

- 배움을 통해현실의 일부를 알게 되며 이를 활용해 세계의 새로운 모델을 구축한다.

1. 매게변수 조정을 통해 학습하는 것

2. 조정가능한 매게변수가 많은 수록 많은 가능성을 지닌다.

- 뇌의 매게변수 즉 신경세포는 860억개의 이르며 1만가지 시냅스 접촉이 일어난다.

- 언어를 배운다는 것은 모든 차원에서의 매게변수 조합을 찾는 과정이다.

3. 에러를 최소화 하는 것

4. 가능성의 공간을 탐구하는 것

5. 보상 기능을 최적화 하는 것

6. 검색공간 제한

- 학습을 촉진시키는 방법은 모델을 최적화 시키는 것이다.

7. 선험적 가설을 투영하는 것 

- 아기들의 뇌는 태어날때부터 조직화되어있고 지식도 갖고 있다. 즉 선천적인 가설, 지식 제약이 존재하며 후천적으로 특정한 매게변수 조정을 통해 학습하는 것이다.

- 우리외는 온갖 추정들로 빛어져있다.

- 배움은 늘 선험적 사설로 시작하여 그 가설의 투영 그리고 데이터로부터 현재 환경에 가장 적합한 데이터를 선별하는 것이다.

 

  • 우리의 뇌는 왜 기계보다 잘 배울까?

딥러닝이 높은 평가를 받지만 한계는 명확할 것이다. 극히 짧은 순간에 무의식적으로 수행해 내는 일들만 할 수 있다.

 

  • 인공지능이 놓치고 있는 것

1. 추상적 개념학습 - 인간의 학습은 단순패션인식이 아닌 추상적 모델을 구축하는 것이다.

2. 데이터 효율성 높은 학습 - 인간은 최소한의 데이터로 최대한의 효과를 거둔다.

3. 소셜러닝 - 인간은 자발적으로 정보를 공유하는 유일한 종이다.

4. 단 한번만의 학습 - 인간이 가진 효율적 학습 능력의 극단적이 예이다.

5. 체계화 능력과 생각의언어 - 유한한 수단들을 무한하게 활용하는 능력을 갖는다.

6. 구성 - 인공지능은 극단적으로 좁은 문제만 해결하지만 인간은 배운것을 재활용, 재결합 할 수 있고 타인에게 설명할 수도 있다.

 

 

  • 아이들이 태어날때부터 가진 두 요소

 

아이들은 단어를 처음 배우기도 전에 일종의 생각언어 같은 것을 가지고 있어 추상적 가설을 만들고 짐작할 수 있다. 아이들은 백지상태가 아니다. 선천적인 지식덕분에 아이들은 추상적인 공간을 자신이 배우는 것만으로 제한 할 수 있고 이러한 메타규칙 덕에 아이들은 자동으로 점점 발전해간다.

 

1. 각종 추상적 공식을 만드는 능력

2. 그 공식들 중에 지혜롭게 선택해 쓸 수 있는 능력

아이들의 뇌는 엄청난 생성 모델이다. 거대하게 조직적이며 수많은 가설들과 구조들을 만드는 능력을 가진 뇌이다. 게다가 스스로 현실에 맞는 공식을 선택한다.

 

  • 성인 판단력의 두 차원

1. 선천적 지식 - 진화과정에서 물려받은 선행지식

2. 개인적 경험 - 삶 속 추론에 토대를 둔 가설들의 수정, 후속지식

뇌조직은 우리에게 강력한 스타트업 키드이며 강력한 학습머신이다.

 

2부 우리의 뇌가 배우는 법

  • 아기들의 보이지 않는 지식

- 미성숙한 것은 사실이나 신생아의 뇌에는 역사를 통해 물려받은 상당한 핵심지식이 있다. 이는 각종 실험을 통해 발견된다.

- 물체개념, 숫자감각, 확률 직관력, 동물과 사람에 대한 지식, 얼굴인식 등

 

  • 뇌의 탄생

- MRI의 등장으로 인해 신생아의 뇌에 성인 뇌 회로가 전부 이미 존재한다는 사실이 밝혀졌다. 날때부터 잘 조직화 되어있어 시각,촉각,청각 영역을 활성화시킨다.

 

  • 뇌가소성

- 피아나스트가 되려면 장기간의 정신적,신체적 훈련이 필요하다. 그러기 위해 미리 구축된 유기적 경로들이 강화되어양 하고 말단 수상돌기 및 신경돌기의 점진적 성장과 세분화를 통해 새로운 경로들이 만들어져야 한다.

뇌의 조직화는 경험을 통해 개선,강화된다.

- 신경세로,시냅스,마이크로회로들은 뇌가소성의 물질적 하드웨어이며 시냅스의 존재와 수,강도 수상돌기와 축삭돌기의 크기와 가시수, 신경전송속도를 결정하는 미엘린의 수까지 변화할 수 있다.

- 활성화되는 신경세포들은 함께 연결되고 강화되며 이 후 훨씬 더 효율적인 신경전달이 가능하게 된다.

=> 학습에서 시냅스 가소성은 매우 중요하다!

- 가소성은 무한한것이 아닌 영양분과 에너지를 필요로 하는 철저히 물질적인 과정이다.

 

  • 민감기란 무엇인가?

- 가소성이 최고조에 달하는 민감기는 어린 시절에 절정에 달하며 나이가 들며 점차 준다.

- 아이들은 학습기계이며 성빈보다 2배의 시냅스 밀도를 갖는다.

- 나이가 들면 뇌가소성이 떨어져 배우는 것이 멈추지는 않지만 점점 힘들어지는 이유이다.

- 감각영역들은 높은 수준의 피질영역들보다 빨리 성숙한다. 이는 피질 내 초기 감각영역의 조직화를 마무리 한 후 뇌의 입력을 최대한 빨리 안정시켜서 상위계층의 피질영역을 훨씬 오래 변화되게 한다. 이는 조직화의 원칙으로 보인다.

- 언어학습은 이를수록 좋다. 문법학습을 위한 뇌가소성이 사춘기가 끝날 때 쯤이면 현저히 준다.(10세 이전에 시작한 것을 권장한다고 한다.)

- 언어의 문법,음에 대한 학습능력을 일찍 끝난다 해도 어휘학습에 관한 능력은 평생 가능한 상태가 유지된다. 왜 어휘에는 민감기를 타지 않는지 그 생물학적인 원인은 밝혀진바 없다.

 

  • 당신의 뇌를 재활용하라.

뇌 회로들은 진화과정 속에 유전된 해부학적 제약들에 영향을 받는다. 따라서 새로운 것을 만들때 뇌회로들 사이에 그것이 들어갈 틈새를 찾아야 한다. 기존 뇌 회로 구조를 변경하고 특성을 재활용하는 것이다.

교육은 안긴 뇌 회로의 다양성과 가소성을 활용하되 뇌 회로의 본질적인 한계 내에서 이루어진다.

 

  • 풍요로운 환경의 이점

태어나서 몇년간 신경회로는 2배가까이 과잉생산하기에 외부 세계 모델을 수용할 공간이 얼마든지 있다. 따라서 어린아이의 뇌는 온갖 가능성으로 차고 넘친다. 성신보다 훨씬 폭넓은 가설들을 탐구한다.(유전적 한계 안에서) 게다가 강력한 학습 알고리즘에 의해 유용한 시냅스들과 회로들은 선택되어 한경에 잘 적응해간다.

 

  • 모든 연구결과가 시사하는 바

=> 환경을 풍요롭게 해주면, 그 아이가 더 나은 뇌를 가진다.

 

  • 배움의 4가지 기능

1. 주의 - 정보확대

2. 적극적 참여 - 호기심, 가설 테스트

3. 에러 피드백 - 예측, 현실 비교를 통한 모델링

4. 통합 - 수면!

문제

한수는 크기가 2N × 2N인 2차원 배열을 Z모양으로 탐색하려고 한다. 예를 들어, 2×2배열을 왼쪽 위칸, 오른쪽 위칸, 왼쪽 아래칸, 오른쪽 아래칸 순서대로 방문하면 Z모양이다.

N > 1인 경우, 배열을 크기가 2N-1 × 2N-1로 4등분 한 후에 재귀적으로 순서대로 방문한다.

다음 예는 22 × 22 크기의 배열을 방문한 순서이다.

N이 주어졌을 때, r행 c열을 몇 번째로 방문하는지 출력하는 프로그램을 작성하시오.

다음은 N=3일 때의 예이다.

입력

첫째 줄에 정수 N, r, c가 주어진다.

출력

r행 c열을 몇 번째로 방문했는지 출력한다.

제한

  • 1 ≤ N ≤ 15
  • 0 ≤ r, c < 2N

 

접근방법 1

처음에는 count 변수를 두어서 첫 0번째 부터 방문하여서 (r,c)에 해당하는 인덱스를 찾을때 까지 반복했다.

n이 최대 2의 15승인지라 0부터 찾아가기에는 당연히 시간초과가 걸렸다.

#include <iostream>
#include <cmath>
using namespace std;

int n,r,c,count=0;
int result;

void solve(int px, int py, int length){
  
  if(length==2){
    if(px==r && py==c){
      result = count;
      return ;
    }
    count++;
    if(px==r && py+1==c){
      result = count;
      return ;
    }
    count++;
    if(px+1==r && py==c){
      result = count;
      return ;
    }
    count++;
    if(px+1==r && py+1==c){
      result = count;
      return ;
    }
    count++;
    return ;
  }
  solve(px,py, length/2);
   solve(px,py+length/2, length/2);
  solve(px+length/2,py, length/2);
  solve(px+length/2,py+length/2, length/2);
  return ;
}

int main(){
  scanf("%d %d %d", &n, &r, &c);
  solve(0,0,pow(2,n));
  printf("%d\n", result);
}

 

접근방법2

- 0부터 순차적으로 찾기보다는 재귀가 뻗어가는 4부분중에 어느부분에 r,c가 위치하는 지를 판단하여 적극적으로 찾는 방법을 채택했다.

- 각 탐색 색션에 따라 4분면의 인덱스 개수는 (len/2)^2으로 고정되어 있으므로 이를 계산해주면서 재귀를 진행한다.

- ex. n=3인 경우 첫 len은 8이 된다. 이때 각 사분면에 들어갈 수 있는 인덱스 개수는 (8/2)^2 = 16이다.

#include <iostream>
#include <cmath>
using namespace std;

int n,r,c,count=0;

int solve(int x, int y, int len){
  if(x==r && y==c) return 0;
  if(x<=r && r<x+len/2 && y<=c && c<y+len/2) return 0+solve(x,y,len/2);
  if(x<=r && r<x+len/2 && y+len/2<=c && c<y+len) return pow((len/2),2)*1+solve(x,y+len/2,len/2);
  if(x+len/2<=r && r<x+len && y<=c && c<y+len/2) return pow((len/2),2)*2+solve(x+len/2,y,len/2);
  if(x+len/2<=r && r<x+len && y+len/2<=c && c<y+len) return pow((len/2),2)*3+solve(x+len/2,y+len/2,len/2);

  return 0;
}

int main(){
  scanf("%d %d %d", &n, &r, &c);
  int result = solve(0,0,pow(2,n));
  printf("%d\n", result);
}

 

생각을 더 깊이하여 새로운 방법을 찾아내는 경험은 항상 뿌듯하다.

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

 

2146번: 다리 만들기

여러 섬으로 이루어진 나라가 있다. 이 나라의 대통령은 섬을 잇는 다리를 만들겠다는 공약으로 인기몰이를 해 당선될 수 있었다. 하지만 막상 대통령에 취임하자, 다리를 놓는다는 것이 아깝다

www.acmicpc.net

 

문제접근 및 풀이

기본적으로 BFS로 접근했다.

 

2개의 섬만을 잇는 최단경로를 구해야 하므로 바다와 인접한 칸(테두리 칸이라고 하겠다)들을 구별했다.

그 후 테두리칸들에 대해 서로 bfs를 진행하면서 최단경로를 구하는 방식으로 해결했다.

 

step1 : 테두리 칸에 번호부여하기

- 부여받은 인풋배열 b[][]를 돌면서 각 섬에 대해 인덱싱(v[][])을 진행한다. 이때 바다와 인접하지 않은 지역은 -1로 설정해준다.

- 섬마다 테두리칸들을 구별해야 하므로 1,2,3,...이런식으로 증가하는 인덱스값을 부여해주었다.

 

step2 : 각 테두리 칸에 대해 BFS탐색하여 다른섬으로 가는 최단경로 탐색하기

- 테두리칸들에 대해 서로 bfs를 진행한다.

- 인덱싱한 값을 매게변수로 넘겨주어서 같은 섬의 테두리는 탐색하지 않도록 한다.

- 바다인 칸 혹은 다른 섬의 테두리만 탐색한다.

 

#include <iostream>
#include <queue>
#include <algorithm>
#define MAX 100 

using namespace std;

int n,key=1, result=987654321;
int b[MAX][MAX]={0};
int v[MAX][MAX]={0};
int k[MAX][MAX]={0};
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};

void bfs(int i, int j, int key){
    queue<pair<int,int>> q;
    q.push({i,j});
    v[i][j]=1;
    while(!q.empty()){
        int find=0;
        int x = q.front().first;
        int y = q.front().second;
        q.pop();
        for(int i=0; i<4; i++){
            int nx = x + dx[i];
            int ny = y + dy[i];
            if(nx>=0 && ny>=0&&nx<n&&ny<n){
                if(v[nx][ny]==0 && b[nx][ny]==1){
                    v[nx][ny]=1;
                    q.push({nx,ny});
                }
                if(b[nx][ny]==0){//b = 0 바다라면
                    k[x][y] = key;
                    find=1;
                }
            }
        }
        if(find==0){
            k[x][y]=-1;
        }
    }
    return ;
}

void bfs2(int i, int j, int key){
    //탐색조건 k[][] 가 -1(육지) 혹은 key(같은 테두리)가 아니라면 탐색
    int v[MAX][MAX]={0};
    queue<pair<pair<int,int>, int>> q;
    q.push({{i,j},0});
    v[i][j]=1;
    while(!q.empty()){
        int x = q.front().first.first;
        int y = q.front().first.second;
        int c = q.front().second;
        q.pop();
        if(k[x][y]!=0 && k[x][y]!=key){
            result = min(result, c);
            return ;
        }
        
        for(int i=0; i<4; i++){
            int nx = x + dx[i];
            int ny = y + dy[i];
            if(nx>=0 && ny>=0&&nx<n&&ny<n){
                if(v[nx][ny]==0 && k[nx][ny]>=0 && k[nx][ny]!=key){ // 메모리초과 -> v체크안했었음
                    v[nx][ny] = 1;
                    q.push({{nx,ny}, c+1});
                }
            }
        }
    }
    return ;
}

int main()
{
    cin >> n;
    for(int i=0; i<n; i++){
        for(int j=0; j<n; j++){
            scanf("%d", &b[i][j]);
        }
    }
    
    //step1 : 테두리 번호 부여하기 
    for(int i=0; i<n; i++){
        for(int j=0; j<n; j++){
            if(b[i][j]==1 && v[i][j]==0){
                bfs(i,j,key);
                key++;
            }
        }
    }
    //step2 : 각 테두리 칸에 대해 bfs하여 다른 테두리 카능로 가는 최단거리 탐색 
    for(int i=0; i<n; i++){
        for(int j=0; j<n; j++){
            int key = k[i][j];
            if(key > 0){ // key가 0보다 크다면 번호가 부여된 테두리이다.
                bfs2(i,j,key);
            }
        }
    }
    
    cout << result-1;
    
    return 0;
}

 

느낀점

- 생각보다는 간단하게 해결되는 문제이지만 생각을 충분히 한 후에 단박에 풀어낼 수 있어서 뿌듯했다.

'알고리즘 문제풀이' 카테고리의 다른 글

[백준] 2475번 검증수 c++  (0) 2022.02.04
[백준] 1074번 Z c++  (0) 2022.01.25
[백준] 15663 N과 M (9) c++  (0) 2021.09.04
[백준] 15665번 N과 M (11) c++  (0) 2021.09.04
[백준] 20040번 사이클 게임 c++  (0) 2021.08.30

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

 

15663번: N과 M (9)

한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해

www.acmicpc.net

문제접근 및 풀이

기본적인 백트래킹문제이다.

 

고려해야할 사항이 몇가지 있다.

1. 사전 순으로 증가하는 순서로 출력해야 한다.

-> input 배열을 sort하고 백트래킹시에 0번 인덱스부터 접근한다.

 

2. 중복된 경우를 허용하지 않는다.

-> mul이라는 변수를 사용해서 바로 이전에 선택한 숫자를 저장하여 for문 안에서 mul과 겹친다면 넘어가도록 하여 중복을 없앤다.

 

3. 각 숫자는 한번만 사용가능하다.(N개의 자연수 중 M개의 숫자를 뽑는 경우이기 때문이다.)

-> v배열을 두어 check를 하며 백트래킹을 진행한다.

 

 

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int n,m;
vector<int> input;
vector<int> result;
int v[8]={0};

void bt(int depth){
  if(depth==m){
    for(int i=0; i<result.size(); i++){
      printf("%d ", result[i]);
    }
    printf("\n");
    return ;
  }
  int mul = -1;
  for(int i=0; i<n; i++){
    if(v[i]==1 || mul == input[i]) continue;
    mul = input[i]; 
    v[i]=1;
    result.push_back(input[i]);
    bt(depth+1);
    v[i]=0;
    result.pop_back();
  }
  return ;
}

int main(){
  cin >> n >> m;
  for(int x,i=0; i<n; i++){
    scanf("%d", &x);
    input.push_back(x);
  }
  sort(input.begin(), input.end());
  bt(0);
  return 0;
}

+ Recent posts