<링크>

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



지금으로부터 6달 전, 2018년 1월 5일에 풀었었는데 이 때에 비해 실력이 얼마나 늘었는지 궁금해서

똑같은 문제를 또 봤다. 많이 틀리고 개고생했던 기억이 난다.


<2018.01.15 소스코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Scanner;
 
public class Main {
    public static void main(String[] args) {
 
        // 61453
        // 7 8 9
        Scanner in = new Scanner(System.in);
        String num = in.next();
        int channel = Integer.parseInt(num);
 
        int K = in.nextInt();
        LinkedList<Integer> list = new LinkedList<Integer>();
        for (int i = 0; i < 10++i)
            list.add(i);
 
        for (int i = 0; i < K; ++i)
            list.remove(new Integer(in.nextInt()));
        // list정렬
        Collections.sort(list);
        int nogada = Math.abs(channel - 100);
    
        if (K == 10) { // 칠수있는 번호가 하나도없을시 (다고장)
            System.out.println(Math.abs(channel - 100));
            return;
        }
 
        int mini = channel;
        String min = null;
        boolean check1 = false,check2=false;
        while (mini >= 0) {
            min=Integer.toString(mini);    
            if(channel-mini+min.length()>=nogada)
                break;
        
            check1 = true// 성공했다고 가정
            for (int j = 0; j < min.length(); ++j) {
                int n = min.charAt(j) - '0';
                if (list.indexOf(n) == -1) {
                    check1 = false;
                    break;
                } // 실패
            }
            if (check1 == true)
                break;
            else
                --mini;
        }
        int maxi=channel;
        String max;
        while (true) {
            max=Integer.toString(maxi);    
            if(maxi-channel+max.length()>=nogada)
                break;
            check2 = true// 성공했다고 가정
            for (int j = 0; j < max.length(); ++j) {
                int n = max.charAt(j) - '0';
                if (list.indexOf(n) == -1) {
                    check2 = false;
                    break;
                } // 실패
            }
            if (check2 == true)
                break;
            else
                ++maxi;
        }
        mini=channel-mini+min.length();
        maxi=maxi-channel+max.length();
        
        int result=nogada;
        if(maxi<result && check2){
            result=maxi;
        }
        if(mini<result && check1){
            result=mini;
        }
        System.out.println(result);
        
        // 만들수있는숫자 i가 나옴
 
    }
}
 
cs


<풀이>

뭐저렇게 복잡해보이는지 잘 모르겠다. 꼴보기 싫어서 그냥 새로 풀었다.




<2018.07.26 소스코드-1>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include<stdio.h>
#include<vector>
#include<math.h>
using namespace std;
vector<int> v;
int seq[6];
int X[10];
int ans, N, M;
void check();
void dfs(int);
int main()
{
    scanf("%d%d"&N, &M);
    for (int i = 0; i < M; ++i)
    {
        int n;
        scanf("%d"&n);
        X[n] = 1// 1고장난애들
    }
    ans = abs(N - 100);
    v.push_back(0); //0은 무조건 추가, 자리수관리용
    for (int i = 1; i < 10++i)
        if (!X[i])
            v.push_back(i); //누를수있는 버튼을 벡터에 추가
    dfs(0);
    printf("%d",ans);
}
void dfs(int len)
{
    if (len == 6)
    {
        check();
        return;
    }
    for (int i = 0; i < v.size(); ++i)
    {
        seq[len] = v[i];    
        dfs(len + 1);
    }
}
void check()
{
    int i = 0;
    int cnt = 6;
    while (i < 5 && seq[i] == 0//앞에 붙은 0들을 제거
    {
        ++i;
        --cnt;
    }
    int channel = 0;
    while(i<6)
    {
        if (seq[i] == 0 && X[0]) //앞의 0을 다 제거했으므로 이제 사이에 나오는 0은 0버튼이 고장났는지 여부에 따라 갈림
            return;
        channel += seq[i] * pow(105 - i);
        ++i;
        
    }
    
    if (abs(N - channel) + cnt < ans)
        ans = abs(N - channel) + cnt;
 
}
cs
<풀이>

리모콘 버튼을 하나 선택하고 다음자리 수를 재귀를 돌렸다. 최대 6자리까지만 했다.

N의 최대입력값이 500000이기 때문에 999900 이상되는 수부터는 +,-를 이용해서 오는건, 100번에서 +,-만 이용해서 오는 것보다 구리기 때문이다.


시간복잡도는 10^6이다.


맨 처음 채널에 100이기 때문에 100에서부터 원하는 채널 N까지 +,-를 이용해 가는 값, 즉 |N-100| 으로 최솟값을 일단 정해놓고 고장이 안난 버튼으로 재귀를 돌다가 depth가 6이 되면 최솟값을 갱신했다.


예를 들어

고장난 번호가 3,4,5,6,7,8,9 일 때 가능한 번호는 0,1,2이고


000000

000001

000002

...

000012

...

222220

...

222222

이렇게 모두 체크해본다.


000012를 체크하면, 앞의 0은 누른 횟수로 치지않고 2번 누른 것으로 한다.


근데 여기서 0 버튼이 고장나면 문제가 발생한다.

고장이 안난 버튼으로만 재귀를 돌렸기 때문에

111111

111112

...

222222 이렇게 체크하게 되는데

그럼 6자리가 아닌 채널들은 체크를 못하게된다.

그래서 0은 무조건 재귀돌릴 때 추가하고,  체크할 때 걸러내게끔 했다

000012 -> 얘는 앞자리채우기로써 0이 들어온거니까 : ㅇㅋ

011012 -> 중간에 유효숫자로써 0이 들어온거니까 : ㄴㄴ



<2018.07.26 소스코드-3>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<string>
int check(int);
using namespace std;
int X[10];
int ans, N, M;
int main()
{
    scanf("%d%d"&N, &M);
    for (int i = 0; i < M; ++i)
    {
        int n;
        scanf("%d"&n);
        X[n] = 1//고장난애들
    }
    ans = abs(100 - N);
    for (int i = 0; i <= 1000000++i)
    {
        int len = check(i);
        if (len)
            ans = min(abs(N - i) + len, ans);
    }
    printf("%d", ans);
}
int check(int num)
{
    string s=to_string(num);
    int len = s.length();
    for (int i = 0; i < len; ++i)
    {
        if (X[s[i] - '0'])
            return 0;
    }
    return len;
}
cs

<풀이>

새롭게 재귀로 풀고나서 좀 늘었구나 싶었는데 

다른 사람들의 소스코드를 살펴보고나서 깨달음을 얻었다.

그냥 0번부터 100000번까지 for문으로 쫙 돌려보며 

가능한 숫자버튼으로 만들수 있는 채널이면 최솟값을 갱신시키는 방식이다.

코드가 80->60->40 줄로 짧아져서 기쁘긴한데 난 아직 멀었다.


<링크>

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


<소스코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include<stdio.h>
#include<vector>
using namespace std;
int isVisited[9];
int N;
void dfs();
vector<int> v;
int main()
{
    scanf("%d"&N);
    dfs();
}
void dfs()
{
    if (v.size() == N)
    {
        for (int i = 0; i < v.size(); ++i)
            printf("%d ", v[i]);
        printf("\n");
    }
    for(int i=1;i<=N;++i)
        if (!isVisited[i])
        {
            isVisited[i] = 1;
            v.push_back(i);
            dfs();
            isVisited[i] = 0;
            v.pop_back();
        }
}
cs


<풀이>


visit를 확인하면서 방문 안한 숫자를 넣으며 재귀를 돈다.

depth를 들어가면서

visit를 체크해주고, 벡터의 맨끝에 새로 추가될 숫자를 넣어준다.

복귀하면서. visit 체크를 해제해주고 벡터의 맨 끝 숫자를 빼준다.

 

<링크>

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

 

<소스코드>

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include<stdio.h>
#include<algorithm>
using namespace std;
int a[10000];
int main()
{
    int N;
    scanf("%d"&N);
    for (int i = 0; i < N; ++i)
        scanf("%d", a + i);
    int i = N - 1;
    while (i > 0 && a[i - 1> a[i])
        --i;
    if (i == 0)
    {
        printf("-1"); return 0;
    }
    int near = 10001;
    int index;
    for (int j = i; j < N; ++j)
    {
        if (a[i - 1< a[j] && a[j] < near)
        {
            near = a[j];
            index = j;
        }
    }
    int tmp = a[i - 1];
    a[i - 1= a[index];
    a[index] = tmp;
    sort(a + i, a + N);
    for (int i = 0; i < N; ++i)
        printf("%d ", a[i]);
}
cs
 

 

 

<풀이>


N이 10000이기 때문에 모든 순열을 하나하나 구해보면 N!의 시간이 걸리고 당연히 시간초과이다.그래서 하나하나 적어보며 규칙성을 찾았다.
1.

 맨 뒤에서부터 시작해서 내림차순이 끊기는 지점을 찾아낸다.

 

1 2 3 4

 

1 2 4 31 3 2 41 3 4 21 4 2 31 4 3 22 1 3 42 1 4 32 3 1 4...3 4 2 14 1 2 3...4 3 2 1

2.

 끊긴 지점에 있는 수가 증가되어야 하는데 조건이 있다. 앞에 나왔던 숫자는 제외하고, 뒤에 있는 숫자 중 크면서 가장 가까운 수로 바뀐다.

 

예를 들어보면

 

 

1이 증가되어야하는데 2로 될 수는 없다. 앞에 2가 있기 때문이다.

그럼 앞에 있는애들(2) 눈치 안보고 바뀔 수 있는 놈들은 뒤(4, 3)에 다 몰려있다.뒤에 있는 놈들 중에 누구로 바뀌면 될까 생각해봤는데 4로 바뀌면 안된다.2 1 x x 에서 바로 2 4 x x로 건너뛰는 셈이 되기 때문이다.
3으로 바뀌면2 3 x x 가 되고원래 뒤에있던 3의 자리에는 당연히 1이 들어가야한다.  즉, 맞바뀌게 된다. -> 2 3 4 1

3.

 숫자를 맞바꾸고 나면 끊긴 지점 뒤로 오름차순 정렬을 해준다.3 뒤로 오름차순정렬을 해서 2 3 1 4를 만든다.

4.

 끊긴 지점이 맨 앞이면, 맨 마지막 수열이므로 -1을 출력한다.

근데 사실 이런 고생을 안하고 풀수있는 방법이 있다. STL이 따로 있었다..

next_permutation( )

<소스코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<stdio.h>
#include<algorithm>
using namespace std;
int a[10000];
int main()
{
    int N;
    scanf("%d"&N);
    for (int i = 0; i<N; ++i) 
        scanf("%d", a+i);
    if(next_permutation(a, a+ N))
        for (int i = 0; i<N; i++)
            printf("%d ", a[i]);
    else printf("-1");
}
 
cs

 

<링크>

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


<소스코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Scanner;
 
public class Main {
    static int N,M;
    static ArrayList<Integer> adj[]=new ArrayList[1001];
    static boolean isVisited[]=new boolean[1001];
    
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        for(int i=1;i<=1000;++i)
            adj[i]=new ArrayList<Integer>();
        
        N=in.nextInt();
        M=in.nextInt();
        for(int i=0;i<M;++i) {
            int a=in.nextInt();
            int b=in.nextInt();
            adj[a].add(b);
            adj[b].add(a);
        }
        int ans=0;
        for(int i=1;i<=N;++i) {
            if(!isVisited[i]) {
                isVisited[i]=true;
                ++ans;
                dfs(i);
                
            }
        }
        System.out.println(ans);
    }
    static void dfs(int i) {
        int len=adj[i].size();
        for(int k=0;k<len;++k)
        {
            int toi=adj[i].get(k);
            if(!isVisited[toi]) {
                isVisited[toi]=true;
                dfs(toi);
            }
        }
    }
}
 
cs


<풀이>

1. for문을 돌면서 모든 노드를 시작점으로 dfs를 한다.

2. 한 번 방문했던 노드는 다시 보지 않는다. 시작점 노드도 예외는 없다.

3. dfs를 총 몇 번 했는지 세어주면 답이 나온다.




'알고리즘 풀이 > DFS' 카테고리의 다른 글

백준 10819 차이를 최대로  (0) 2018.07.27
백준 10974 모든 순열  (2) 2018.07.24
KOITP 큰수만들기  (0) 2018.07.23
백준 11403 경로 찾기 :: 들짐승  (0) 2018.07.23
백준 1240 노드사이의 거리 ::들짐승  (0) 2018.07.22

<링크>

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


<소스코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include<stdio.h>
#include<vector>
#include<string.h>
using namespace std;
int isVisited[101];
int N;
vector<int> adj[101];
int ans[101][101];
void dfs(intint);
int main()
{
    int tmp;
    scanf("%d"&N);
    for (int i = 1; i <= N; ++i)
        for (int j = 1; j <= N; ++j)
        {
            scanf("%d"&tmp);
            if (tmp)
                adj[i].push_back(j);
        }
    for (int i = 1; i <= N; ++i)
    {
        memset(isVisited,0,sizeof(isVisited));
        isVisited[i] = 1;
        dfs(i,i);
    }
    for (int i = 1; i <= N; ++i) {
        for (int j = 1; j <= N; ++j)
            printf("%d ", ans[i][j]);
        printf("\n");
    }
}
void dfs(int r,int i)
{
    int s = adj[i].size();
    for (int k = 0; k < s; ++k)
    {
        int toi = adj[i][k];
        ans[r][toi] = 1;
        if (!isVisited[toi])
        {
            isVisited[toi] = 1;
            dfs(r, toi);
        }
    }
}
 
cs


<풀이>

각 노드를 시작점으로 모두 dfs를 돌렸다. 한 번 방문한 노드는 다시 볼 필요가 없다.

dfs의 인자로 시작점을 계속해서 넘겨주고 [시작점][현재노드]=1 로 갱신시켰다.



+ Recent posts