문제 링크

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

 

14889번: 스타트와 링크

예제 2의 경우에 (1, 3, 6), (2, 4, 5)로 팀을 나누면 되고, 예제 3의 경우에는 (1, 2, 4, 5), (3, 6, 7, 8)로 팀을 나누면 된다.

www.acmicpc.net

이전에 풀었던 스타트와 링크 문제를 순열을 이용해 다시 풀어봤다.

각 팀별로 수가 정해져있기 때문에 순열을 활용해 풀 수 있다. 참고로 링크와 스타트 문제 같은 경우에는 팀원의 수가 정확하게 정해져있지 않으므로 순열을 통한 풀이가 불가능하다. 

 

전체 인원을 절반으로 나누어 앞의 절반은 0으로 뒤의 절반은 1로 마킹한 후 순열을 이용해 다음 순열을 넣어가며

전체를 탐색하도록 코드를 짰다.

매 정답이 구해질 때마다 기존의 값이랑 비교해서 더 값이 작다면 그 값이 정답으로 들어가도록 풀었다.

 

해결 코드 

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int n;

int solve(vector<vector<int> > &a, vector <int> &b){
    vector <int> zero,one;
    for(int i=0;i<n;++i){
        if(b[i])
            one.push_back(i);
        else
            zero.push_back(i);
    }
    int z=0; int o=0;
    for(int i=0;i<n/2;++i){
        for(int j=0;j<n/2;++j){
            if(i==j)
                continue;
            z+=a[zero[i]][zero[j]];
            o+=a[one[i]][one[j]];
        }
    }
    int diff=abs(z-o);
    return diff;
}

int main () {
    ios_base::sync_with_stdio(0); cin.tie(NULL);
    //input 받기
    cin>>n;
    vector<vector<int> > arr(n,vector<int>(n));
    for(int i=0;i<n;++i){
        for(int j=0; j<n; ++j){
            cin>>arr[i][j];
        }
    }
    //로직
    vector <int> b(n);
    for(int i=0;i<n/2;++i){
        b[i]=1;
    }
    sort(b.begin(),b.end());
    int ans=999999999;
    do{
        int diff=solve(arr,b);
        if(ans>diff) ans=diff;
    }while(next_permutation(b.begin(),b.end()));
    //output
    cout<<ans<<'\n';
    return 0;
}

@Scheduled  어노테이션

주기적으로 해야되는 작업을 쉽게 적용할 수 있도록 도와주는 어노테이션

스프링 3.1 이상부터 지원 

 

@Scheduled  사용 준비

@SpringBootApplication 어노테이션이 있는 어플리케이션 자바 파일에 @EnableScheduling 어노테이션 추가

@SpringBootApplication
@EnableScheduling
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

@Scheduled  사용 

1. cron 

cron 표현식을 지원

초 분 시 일 월 주(년) 순으로 표기 

@Scheduled(cron = "0 0 9 * * *") //매일 9시마다
public void TestScheduler(){
        System.out.println("테스트");
}
0 ~ 59
0 ~ 59
0 ~ 23
1 ~ 31
1 ~ 12 
요일 1 ~ 7 (1 => 일요일, 7=> 토요일) 
년도 1970 ~ 2099

* : 모든 값

? : 특정 값 없음

- : 범위 지정에 사용

, : 여러 값 지정 구분에 사용

/ : 초기값과 증가치 설정에 사용

L : 지정할 수 있는 범위의 마지막 값

W : 월~금요일 또는 가장 가까운 월/금요일

# : 몇 번째 무슨 요일 2#1 => 첫 번째 월요일

 

사용 예시

 "0 0 02 * * ?" : 매일 새벽 두시

 "0 0 12 * * ?" : 매일 정오

 "0 15 10 ? * *" : 모든 요일, 매월, 아무 날이나 10:15:00 

 "0 15 10 * * ? *" : 모든 연도, 아무 요일, 매월, 매일 10:15 

 "0 0/5 14,18 * * ?" : 아무 요일, 매월, 매일, 14시, 18시 매 5분마다 0초 

 "0 0-5 14 * * ?" : 아무 요일, 매월, 매일, 14:00 부터 매 14:05까지 매 분 0초 

 "0 10,30 14 ? 6 WED" : 6월의 매 수요일, 아무 날짜나 14:10:00, 14:30:00 

 "0 15 10 L * ?" : 아무 요일, 매월 마지막 날 10:15:00 

 "0 15 10 ? * 7L" : 매월 마지막 토요일 아무 날이나 10:15:00 

 "0 15 08 ? * 6L 2002-2005" : 2002년부터 2005년까지 매월 마지막 금요일 아무 날이나 0815:00 

 "0 15 08 ? * 6#3" : 매월 3번째 금요일 아무 날이나 08:15:00

 

2. fixedDelay 

해당 메소드가 종료된 시점부터 다음 메소드 실행 시점까지의 주기

시간 단위는 밀리세컨드

@Scheduled(fixedDelay=1000)
public void TestScheduler(){
        System.out.println("테스트");
}

3. fixedRate

해당 메소드가 시작된 시점부터 다음 메소드 실행 시점까지의 주기

시간 단위는 밀리세컨드

@Scheduled(fixedRate=1000)
public void TestScheduler(){
        System.out.println("테스트");
}

 

 

참고 사이트

https://rooted.tistory.com/12

https://toma0912.tistory.com/17

https://jeong-pro.tistory.com/186

https://java119.tistory.com/34
https://javafactory.tistory.com/1386

현재 일하는 곳에서 개발 중인 앱에 firebase를 통해 푸시 알림을 보내고 sms를 전송하는 부분이 있다. 관련된 부분을 수정을 해야하는 상황이라 fcm이 뭐고 어떤 방식으로 돌아가는지 알아볼 겸 글을 작성하게 됐다. 

FCM이란?

구글에서 무료로 메시지를 안정적으로 전송할 수 있는 교차 플랫폼 메시징 솔루션

앱 서버에서 FCM 서버로 메시지 요청이 가고 이를 받은 FCM 서버가 클라이언트에게 메시지를 보낸다.

쉽게 말해 서비스 회사의 서버와 클라이언트 사이에 FCM 서버가 존재하는 거다.

 

그래 메시지 보내주는 거 알겠어,,근데 왜 이렇게 메시지를 보내는데? 뭐가 다른걸까

 

사용자 김씨에게 어떠한 메세지를 전달해야한다고 가정해보자.

김씨에게 메세지를 어떻게 전달할 수 있을까

서비스 회사의 서버에서 김씨에게 메세지를 보내면 된다.

문제는 김씨가 실시간으로 서버로 부터 메세지를 받으려면 서버에 계속 접속해 있어야 한다.

이러한 방식을 사용하면 배터리도 빨리 딣고 네트워크 사용에도 문제가 발생한다.

이때 FCM을 사용하면 문제를 해결할 수 있다.

중간에 FCM 서버가 끼므로 서비스 회사의 서버에 항상 접속해 있지 않아도 되기 때문이다.

서비스 프로그램이 실행 중이 아니더라도 리스너를 통해 메시지를 수신받을 수 있다.

 

FCM을 통한 메시지 전송 흐름(FE+BE)

1. 클라이언트에서 FCM 서버로부터 FCM 토큰을 요청하고 획득한다.

2. 클라이언트에서 서버한테 해당 토큰을 전달하고 서버는 전달 받은 토큰을 디비에 저장한다.

3. 서버가 전달 받은 토큰을 이용해 FCM 서버로 메시지 전송 요청을 보낸다.

4. 요청을 받은 FCM 서버가 사용자에게 메시지를 전송한다.

5. 사용자가 사용 중인 서비스에서 리스너를 통해 메시지를 받는다.

 

FCM 토큰 만료 케이스

FCM 토큰은 만료되지 않으나, 다음과 같은 특정 케이스들에선 변경된다.

1. 앱 instance ID 삭제

2. 앱 삭제 혹은 재설치

3. 앱 사용자가 앱 데이터 삭제


FCM 라이브러리 추가(BE, build.gradle)

dependencies {
    ...
    
    implementation 'com.google.firebase:firebase-messaging:21.1.0'
}

 

FCM 메시지 종류 2가지

1. notification message

title과 body로 구성

fcm SDK에서 자동으로 처리

{
  "message":{
    "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "notification":{
      "title":"Portugal vs. Denmark",
      "body":"great match!"
    }
  }
}

2. data message

key-value 쌍으로 구성

클라이언트 앱에서 처리

{
  "message":{
    "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "data":{
      "Nick" : "Mario",
      "body" : "great match!",
      "Room" : "PortugalVSDenmark"
    }
  }
}

 

FCM 서버 메시지 요청 방법 4가지

1. Firebase Admin SDK 이용(원시 프로토콜)

Node.js자바PythonC#Go 지원

2. HTTP V1 API 이용

가장 최신 프로토콜, firebase admin sdk는 이 프로토콜을 기반으로

대부분의 사례에 이 API 사용 추천

3. 기존 HTTP API 이용

4. XMPP 서버

전송하는 각 메시지를 고유하게 구별하기 위해 서버에서 메시지 ID를 생성할 수 있어야 함

 

 

 

 

 

 

참고 사이트

https://firebase.google.com/docs/cloud-messaging/

https://developer88.tistory.com/159

https://team-platform.tistory.com/23

https://medium.com/@vdongbin/firebase%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-push-notification-5c8a83932472

https://maejing.tistory.com/entry/Android-FCM%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%B4-Push-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0

 

사용 준비

헤더파일 추가

#include <algorithm>

 

사용

함수에 배열의 주소 또는 벡터의 iterator를 넣어주면 다음 순열과 이전 순열을 구할 수 있다. 

 

next_permutation 함수 

현재 수열에서 인자로 넘어간 범위에 해당하는 다음 순열을 구해주는 함수

다음 순열이 존재해 구할 수 있다면 true 반환 다음 순열이 없다면 false 반환

// 첫번째 인자:순열의 시작, 두번째 인자:순열의 끝
bool next_permutation (BidirectionalIterator first, BidirectionalIterator last);

예시 코드

vector <int> min(k);
do{
      for(int i=0; i<4; i++){
			cout << v[i] << " ";
		}

		cout << '\n'; 
}while(next_permutation(min.begin(),min.end()));

prev_permutation 함수

현재 수열에서 인자로 넘어간 범위에 해당하는 이전 순열을 구해주는 함수

이전 순열이 존재해 구할 수 있다면 true 반환 이전 순열이 없다면 false 반환

//직접 비교함수를 넣어주는 경우
bool next_permutation (BidirectionalIterator first, BidirectionalIterator last, Compare comp);

예시 코드

vector <int> min(k);
do{
      for(int i=0; i<4; i++){
			cout << v[i] << " ";
		}

		cout << '\n'; 
}while(prev_permutation(min.begin(),min.end()));

 

참고 사이트

https://twpower.github.io/82-next_permutation-and-prev_permutation

'프로그래밍 언어 > c++' 카테고리의 다른 글

C++ sort 함수 compare 커스터마이징  (0) 2022.06.03
[STL] 덱(Deque)  (0) 2021.10.12
[STL] 큐(Queue)  (0) 2021.10.11
[STL] vector 컨테이너  (0) 2021.09.19
STL  (0) 2021.09.19

직렬화

자바 객체를 전송 가능한 상태로

 

직렬화 어노테이션들

@JsonAnyGetter

map 필드를 다룰 때 편리

map 필드가 한 번 감싸져서 나옴

public class Member {
    public String name;
    private Map<String, String> properties;

    @JsonAnyGetter
    public Map<String, String> getProperties() {
        return properties;
    }
}

@JsonGetter

@JsonProperty 어노테이션의 대안

메소드의 이름을 getter 메소드로 표현

public class Member {
    public int studentCode;
    private String name;

    @JsonGetter("name")
    public String getTheName() {
        return name;
    }
}

@JsonPropertyOrder

직렬화 하는 속성의 순서를 정함

@JsonPropertyOrder({ "name", "studentCode" })
public class Member {
    public int studentCode;
    public String name;
}

@JsonRawValue

Jackson이 속성을 그대로 직렬화

public class Member {
    public String name;

    @JsonRawValue
    public String deep;
}

output 예시

//적용 후
{
    "name":"My bean",
    "deep":{
        "attr":false
    }
}
//적용 전
{
  "name": "yun",
  "deep": "{\n  \"attr\":false\n}"
}

@JsonValue

@JsonValue 해당 멤버 필드 이름을 통해 직렬화

public enum Member {
    TYPE1(1, "Type A"), TYPE2(2, "Type 2");
    
    private Integer studentCode;
    private String name;

    @JsonValue
    public String getName() {
        return name;
    }
}

@JsonRootName

root wrapper의 이름을 설정할 수 있음

@JsonRootName(value = "user")
public class Member {
    public int studentCode;
    public String name;
}

아웃풋 예시

{
    "user":{
        "studentCode":1,
        "name":"John"
    }
}

 

 

참고 사이트 

https://www.baeldung.com/jackson-annotations

https://pjh3749.tistory.com/281

https://cheese10yun.github.io/jackson-annotation/

 

게시글 목록을 조회할 때 페이지 번호로 목록이 구분되는 걸 많이 본적이 있을 거다.

한 화면에 모든 글을 보여줄 수 없기 때문에 페이지로 나눠 게시글 목록을 보여주는 것인데 오늘은 이 페이징 처리에 관해 글을 써보려고 한다.

 

이 기능을 실제로 하나하나 구현할 시 상당히 번거로운데 Spring Data JPA에서는 Pageable을 이용해 손쉽게 페이징 처리를 할 수 있도록 도와준다. 이떄 단순하게 pagination만 해주는 게 아니라 보여지는 목록에 정렬이 필요한 경우 sorting도 같이 할 수 있다.

 

1. 페이징 처리할 레포지토리에 JPARepositoy를 상속한다.

@Repository
public interface postRepository extends JpaRepository<Post, Long> {
	List<Post> findAll(Pageable pageable);
}

2. 페이징 처리

pageable 객체를 만들어 repository의 메소드에 인자로 전달하면 된다.

Pageable pageable = PageRequest.of(현재 페이지 넘버, 조회할 페이지의 수, 정렬관련정보들);

2-1. 페이징 처리

Pageable pageable = PageRequest.of(page - 1, 10);
repo.메소드명(pageable);

페이지 번호는 0번 부터 시작해 1을 빼줘야한다.

 

2-2. 페이징 처리+정렬

Pageable pageable = PageRequest.of(0, 3, Sort.by("postId").descending());
repo.메소드명(pageable);

 

 

참고자료

 

https://www.baeldung.com/spring-data-jpa-pagination-sorting

https://velog.io/@jjun_meatlov/Spring-Data-JPA-%ED%8E%98%EC%9D%B4%EC%A7%95-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0

https://devlog-wjdrbs96.tistory.com/414

http://devstory.ibksplatform.com/2020/03/spring-boot-jpa-pageable.html

 

+ Recent posts