Linux에서 pdfjam(Link)을 이용하면 확대, 축소, n-up(한 장에 여러 페이지/슬라이드 찍기), 여러 pdf를 하나의 pdf로 합치기(병합)와 같은 작업을 간단히 명령어 한 줄로 수행할 수 있다.

예) pdf를 a4크기에 맞추고 크기를 1.15배 한다: pdfjam --a4paper --scale 1.15 -- my_document.pdf

예) pdf를 2x1(가로 2페이지)로 만들고 싶은 경우: pdfnup --nup 2x1 -- ~/my_document.pdf

예) pdf를 합치는 경우: pdfjoin ~/my_document1.pdf ~/my_document2.pdf

주의) pdfjam으로 편집할 경우, hyperlink가 다 삭제된다고 한다! 인쇄용으로만 추천.

신고

코딩을 너무 안 하는것 같아서 가끔 Codeforces를 풀고있는데, 오랜만에 좋은 문제를 찾았다.


문제를 설명하면 간단하게 N개의 정점과 M개의 가중치를 갖는 무향 간선으로 이루어진 그래프가 주어졌을 때, 모든 정점이 연결돼있도록 N-1개의 간선만 남기되(즉, 스패닝 트리를 만들면 된다), 간선의 가중치의 합이 최소가 되도록 하는 방법을 출력하는 것이다. 여기서 각 그래프에는 Wi라는 가중치와 Ci라는 속성이 있는데, 가중치를 원하는 만큼 줄일 수 있지만(0이나 음수로도 줄일 수 있다), 1씩 줄일 때마다 Ci의 비용이 소요되고, 지불할 수 있는 자금의 한계 S가 주어진다.


Link: http://codeforces.com/contest/733/problem/F


풀이 보기


신고

몇 번 못 해봤지만, 국내에서 취약점을 보고했을 때, 곧바로 진지하게 들어주고 이에 대한 (소소하지만) 보상까지 받아본 경우는 살면서 처음이다(!).

별다른 기대 없이, 취약점이 너무 쉽게 재현 가능하고 돈에 관련된 거라 그냥 넘어갈 수 없어서 제보했던 건데 ㅋㅋㅋ

덕분에 책을 돈 걱정 없이 더 구매할 수 있었다 :D


신고

In [15]:from datetime import datetime; import time

In [16]: datetime.strftime(datetime.strptime('2016-09-20 20:00:13', '%Y-%m-%d %H:%M:%S'), '%Y.%m.%d %H:%M:%S KST')
Out[16]: '2016.09.20 20:00:13 KST'

In [17]: s = datetime.strptime('2016-09-20 20:00:13', '%Y-%m-%d %H:%M:%S')

In [18]: time.mktime(s.timetuple())
Out[18]: 1474369213.0

이건 자꾸 까먹어서 ㅠㅠ

신고

Since Android 5.1 (API Level 22), AlarmManager's setRepeating method forcefully raise intervals under 1 minute (60000 msec) to 1 minute. Although it is completely right decision because waking up phone every 15 seconds or 30 seconds is crazy, but SOMETIMES (for testing, researching, ...) we need such kind of 'bad' alarms. However, we can overcome this shortcoming by setting once-only alarm repetitively(not explicitly calling setRepeating but call set or setExact again and again, like a chain).


For example, previously we have alarms like this:

mAlarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); Intent alarmIntent = new Intent(this, PeriodicWorker.class); alarmIntent.setAction(ACTION_WAKEUP); PendingIntent pendingAlarmIntent = PendingIntent.getService(this, 0, alarmIntent, 0); mAlarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), ALARM_INTERVAL, pendingAlarmIntent);

However, from the API Level 22 (Android 5.1), we have to do it manually like this:

if (mAlarmManager == null) mAlarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
Intent alarmIntent = new Intent(this, PeriodicWorker.class);
alarmIntent.setAction(ACTION_WAKEUP);
PendingIntent pendingAlarmIntent = PendingIntent.getService(this, 0, alarmIntent, 0);
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + ALARM_INTERVAL, pendingAlarmIntent);


신고

귀찮아서 안 쓰고 있었는데, O(nlogn)임에도 계속 시간초과를 받았던게 자꾸 생각나서 늦게나마 해법(결국 Fast I/O까지 써서 시간초과 X)을 올린다 -_-..

문제

철광석과 구리 광석이 묻혀있는 땅이 있다. 여기서 N(1 ≤ N ≤ 100,000)개의 광물(점으로 간주한다)의 좌표가 주어질 때, 어떤 점 P를 원점으로 잡고 1사분면의 구리 광석 개수, 2사분면의 철광석 개수, 3사분면의 구리 광석 개수, 그리고 4사분면의 철광석 개수를 셌을 때의 값을 최대화하도록 P를 정하고, 이때의 합을 출력하자.


파란색 점을 철광석, 갈색 점을 구리 광석이라고 생각하자.

P의 위치를 위와 같이 잡을 경우 합은 3(1사분면) + 3(2사분면) + 3(3사분면) + 2(4사분면) = 11이 된다.


*각 광물의 좌표는 모두 1 ≤ X,Y ≤ N 범위의 정수(자연수)이고, 모든 광물의 X좌표는 서로 다르고, Y좌표도 서로 다르다. 즉, 어떤 수평선 위에도 광물은 최대 한 개 존재하고, 어떤 수직선 위에도 광물은 최대 한 개 존재한다. 그리고, 점 P의 좌표는 실수를 가질 수 있다(즉, 경계선에 놓이는 광물은 없다고 보아도 된다).


풀이

우선 이 풀이를 이해하기 위해선 <Segment Tree> 라는 자료구조에 대한 이해가 필요하다 [링크1] [링크2].

Segment Tree를 처음 배울 때 보통 구간의 합을 구하는 예제를 통해 배우게 되는데, 이를 조금 확장하면 어떤 배열의 Prefix sum, 즉 A[1]+A[2]+...+A[i] (1 ≤ i ≤ N) 의 합이 최대가 되는 i와 그때의 합을 찾을 수 있다. 리프 노드의 최대 접두사 합은 자기 자신의 값으로 하고, 리프가 아닌 노드의 최대 접두사 합은 "왼쪽 자식의 최대 접두사 합"과 "왼쪽 자식의 합 + 오른쪽 자식의 최대 접두사 합" 중 큰 것임이 자명하기 때문에 접두사 합을 저장하는 배열을 하나 더 만들거나, 구조체를 이용해서 세그먼트 트리를 만들면 된다.

세그먼트 트리 코드 보기


이제 위의 자료구조를 이용해서 문제를 해결하는 방법에 대해 살펴보자.


점 P의 X 좌표가 특정한 값(이 값을 Px라고 하자)로 고정되어 있을 경우, 최적의 Y 좌표를 쉽게 구하는 방법은 무엇일까?

직관적으로 쉽게 알 수 있는데, 아래 그림과 같이 배열을 하나 만들고, X좌표가 Px보다 작은 철광석이 있는 Y좌표에는 -1, 구리 광석이 있는 Y좌표에는 +1, 그리고 X좌표가 Px보다 크거나 같은 철광석이 있는 Y좌표에는 +1, 구리 광석이 있는 좌표에는 -1을 준 다음, 접두사 합이 최대가 되는 지점이 바로 Px의 Y좌표가 된다(증명은 귀류법으로 간단히 할 수 있다).


맨 아래에서부터 (-1) + 1 + (-1) + (-1) + 1 + 1 + 1 = 1


이를 처음에 설명한 트리를 응용하여 모든 X좌표(N개(정확힌 N+1개)의 X좌표)에 대해 빠르게(O(logN)) 최적의 Y좌표를 구할 경우, 모든 P의 후보 지점을 살펴보는 데에 O(NlogN)의 시간이 걸리므로 최대 100,000개의 점을 처리하기엔 부족함이 없다.


우선 Px를 나타내는 가상의 수직선을 상상해보고, 다음 그림들을 보자. 


 -> 


이런 상황으로 초기화를 한다(px = 0) - O(NlogN) -> 여기서 최적의 y를 찾는다 - O(1) -> 합을 구한다 - O(1).
(합은 생각해보면, 간단하게 (최대 접두사 합) + (P 오른쪽의 구리 개수) + (P 왼쪽의 철 개수)이다)


이제 수직선을 한 칸 이동시켜서(Px = 1) 반복해보자.

수직선을 이동할 때 갱신해야 하는 것은 세 가지이다:

1. 세그먼트 트리에서 해당 X좌표에 있는 점의 부호를 바꿔준다 - O(logN).

2. 해당 점이 철광석일 경우 P 왼쪽에 있는 철 개수를 저장하는 변수(LEFT_IRON) - O(1)

3. 해당 점이 구리 광석일 경우 P 오른쪽에 있는 구리 개수를 저장하는 변수(RIGHT_COPPER) - O(1).




다시 같은 방식으로 Px=2, ...., Px = N까지 모두 확인해가며 합의 최댓값을 갱신해주면 정답을 구할 수 있다.


정답 코드 보기(base = LEFT_IRON + RIGHT_COPPER)

*10.23: 정식 채점이 가능해짐에 따라 코드 오탈자 수정(정식 채점: http://codeground.org)

테스트 데이터

랜덤하게 만들어본 테스트 데이터와 이에 해당하는 정답 데이터로, 이 데이터에 대한 정답이 0.5초안에 모두 나온지 않는다면 일단 시간초과는 확실한걸로..

sample.in.lzma

sample.out.lzma

(압축 해제를 해야 합니다..)


신고
  1. cujun 2016.07.22 15:22 신고

    작성자만 볼 수 있는 글입니다.

  2. chojl1125 2016.09.11 23:29 신고

    퍼가요~

  3. leejk9592@naver.com 2017.07.20 00:38 신고

    안녕하세요... 일단 좋은 글을 작성해주셔서 감사드립니다.
    해설을 읽던 도중,이해가 가지 않는 부분이 있어 염치불구 하고 질문을 드립니다.


    점 P의 X 좌표가 특정한 값(이 값을 Px라고 하자)로 고정되어 있을 경우, 최적의 Y 좌표를 쉽게 구하는 방법은 무엇일까?

    """
    직관적으로 쉽게 알 수 있는데, 아래 그림과 같이 배열을 하나 만들고, X좌표가 Px보다 작은 철광석이 있는 Y좌표에는 -1, 구리 광석이 있는 Y좌표에는 +1, 그리고 X좌표가 Px보다 크거나 같은 철광석이 있는 Y좌표에는 +1, 구리 광석이 있는 좌표에는 -1을 준 다음, 접두사 합이 최대가 되는 지점이 바로 Px의 Y좌표가 된다
    (증명은 귀류법으로 간단히 할 수 있다).
    """

    제가 이 부분을 하루 정도 고민을 해보고, 테스트를 통해 결과가 최적의 y좌표가 되는 것은 확인을 해보았는데,
    그 이유가 잘 이해되지 않아 귀류법으로 어떻게 증명이 되는지 부탁을 드리려고 댓글을 작성하게 됐습니다.

    여러가지 일로 바쁘실텐데, 답변을 해주지 않으셔도 이해합니다.
    감사합니다

    • kcy1019 2017.07.20 18:23 신고

      안녕하세요, 제가 귀찮음에 글을 부실하게 적어두었네요ㅠㅠ 쓸 당시에 생각해둔 증명은 뭐 엄밀한건 전혀 아니고 직관적이고 단순한 증명으로, 이에 대한 설명은 대부분 알고리즘의 X좌표에 대한 설명과 비슷합니다:

      1. 우선 Py = 0 인 상태에서는 Px 왼쪽의 철광석과 Py 오른쪽의 구리만이 유효하게 카운트됩니다(이 값이 위의 base, 그러니까 LEFT_IRON, RIGHT_COPPER 입니다).

      2. 여기서 이제 Py가 한 칸(즉 가장 가까운 광석의 바로 위로) 올라가면 해당 광석이 Px보다 왼쪽인지 오른쪽인지에 따라, 그리고 광석의 종류가 철광석인지 구리인지에 따라 유효한 광석의 수가 Py = 0일 때 보다 +1이 될 수도 있고, -1이 될 수도 있습니다. 즉 세그먼트 트리에 들어가는 값은 "Py가 이 광석 바로 위일 때 현재 유효한 광석의 수가 변하는 정도" 입니다.

      3. 정리하면, 이 알고리즘은 각 Px에 대해 Py = 0 일 때의 답에서 시작해서 어떤 Py ≠ 0 (정확히는 Py > 0)인 경우의 답들 중 최댓값이 무엇인지 살펴보는 알고리즘 입니다. 즉 Px 가 고정되었을 때 (Py = k일 때의 답) = (Py = 0일 때의 답) + (Py = k일 때 답의 변화량) 과 같은 식으로 계산하는 것이죠.

      4. 이제 증명 비슷한걸 해 봅시다. 최대 접두사 합을 갖는 Y를 OptY라고 하면, 귀류법으로 답이 OptY가 아닌 어떤 Py이고, 그때의 접두사 합이 최댓값이 아니라고 가정할 경우 다음과 같이 살펴볼 수 있습니다.

      우선 Px가 고정되어 있으므로 (Py = 0일 때의 답)은 변하지 않습니다.
      4a. Py < OptY: Py를 OptY까지 올리면 (변화량) 부분이 최소 1 증가합니다.
      4b. Py > OptY: Py를 OptY까지 내리면 (변화량) 부분이 최소 1 증가합니다.
      따라서 Py가 최적의 Y라는 것은 모순이 되어 최적의 Y는 OptY임을 증명할 수 있습니다.

      여담이지만 지금 보니 굳이 귀류법으로 해야 할 필요는 없던 거였네요ㅋㅋㅋ 역시 글로 써 봐야 더 정확해지나 봐요..

      질문 감사합니다!

  4. leejk9592@naver.com 2017.07.22 04:34 신고

    답변 해주셔서 정말로 감사드립니다

Lua를 연습해볼 겸 이전에 PacMan에 사용했던 유전 프로그래밍을 약간 변형해서 적용해봤다. 너무 단순한 방법이라 그런가 성능이 좋은 편은 아니지만, Lua의 coroutine을 연습해보는 정도로 만족. 애초에 더 좋은 알고리즘을 만들려면 이전 상태를 고려하고 탄막의 속도를 계산할 줄 아는 알고리즘을 생각해야겠지(일단 지금 드는 생각은, 사람이 직접 이런 feature를 만드는건 귀찮고 재미도 없을 것 같으니 (귀찮아서 안 할듯 하지만)만약 시도한다면 NEAT나 변형된 Deep Q-Learning을 구현할 생각).

GitHub Link: https://github.com/kcy1019/strikersii_ai (forked from https://github.com/aikorea/strikersii_ai)


신고
  1. Bae0231 2016.03.29 10:28 신고

    제가 하는거 보다 낫네요! 멋있어요

  2. agaga 2016.05.12 01:51 신고

    오! 굳. 잘 만들었네요 요즘 루아 쓰시는군요

토익 성적표가 필요한데 난 프린터가 없어서.. 이걸 PDF로 소장하기 위해 삽질한 기록을 남겨둔다.

인터넷에 여러가지 팁이 있지만, 어째서인지 그게 Windows 7에선 잘 되는데 Windows 8에서는 안 먹히더라 -_- SPL파일 형식이 열어보니 ZIP + XML형식.. 아무래도 OXPS랑 비슷한 구조를 가지고 있어서 그런듯 한데... 어쨌든 의외의 방법으로 성공했다.

일단 준비물:

  • http://lerup.com/printfile/ (SPL Viewer는 "No PCL and Postscript file! Load anyway" 라고 하면서 안 열리지만 이걸로는 잘 된다)
  • 증명서 발급이 가능한 가짜 프린터(대충 고급 프린터 설정(검색에서 찾으세요) -> 원하는 프린터가 없습니다 -> HP나 Canon에서 아무거나 골라 설치하면 된다).

이제 없는 프린터로 TOEIC 성적표를 출력하려고 하면 오류가 나서 당연히 출력이 되지 않는다. 이 때 C:\Windows\System32\Spool\Printers 에 들어가면 XXXXX.SPL과 같은 SPL파일이 있을텐데, 이걸 아무데나 복사해두고, 인쇄를 취소한 다음 토익 성적표 출력 창을 끄자(이 창이 켜져있는 동안엔 ActiveX가 컴퓨터에 큰 장애를 안겨주어 정상적인 작업이 불가능하다).

그 다음에는 PrintFile을 실행하고 Print File을 클릭한 다음 아까의 SPL파일을 클릭하면 출력 대화 상자가 나타나는데, 이 때 한컴의 PDF프린터가 있다면 그걸로 뽑으면 바로 PDF가 되고, 아니라면 MS XPS 프린터로 XPS/OXPS로 뽑은 다음 적당한 인터넷 사이트나 앱(이를테면 맥의 XPS-to-PDF-Lite라든가)을 찾아서 변환하면 된다(XPS로 갖고있어도 뭐.. 윈도우즈에서만 쓸거라면 상관 없고).

신고
  1. cujun 2017.02.07 16:01 신고

    좋은 정보 감사합니다. ^^*
    윈 10에서 MS pdf, XPS writer 또는 Hancom PDF 전부 기본 저장폴더가 "C:\Users\(사용자이름)\문서" 네요. 저장되는 이름 포맷이 Printfile-~.pdf 꼴이니 참고하세염

  2. 우왕 2017.06.09 18:54 신고

    감사합니다 :)

개인적으로 맥북 키보드의 기본 키맵은 꽤나 괜찮다고 생각한다. 하지만.. 키 배열이 다른 외장키보드를 연결할 경우 매우 불편해진다.

이를테면 보통 사용되는 한글 키보드의 경우 스페이스 키 왼쪽에는 CONTROL-COMMAND-OPTION 순서로 키가 매핑되는데, 이게 맥북 기본 키보드의 (FN-)CONTROL-OPTION-COMMAND와 다른 점과, 맥북 키보드의 FN키를 외장 키보드에서 누를 방법이 없는 점이 그렇다(Page up/down이야 직접 키를 누르면 된다고 하자, 화면 밝기 조절이나 볼륨 조절 등은?).

이를 해결할 방법을 찾아보니 거의 모든 답변이 키를 리매핑하는 소프트웨어를 이용하는 것이었는데, 직접 해보니 확실히 편리해지긴 했지만, 그 과정에서 약간의 삽질을 했기 때문에 -_-; 다시 헤매는 일이 없도록 내가 사용한 설정법을 간단하게 적어본다.

1. Karabiner 다운로드 및 설치(https://pqrs.org/osx/karabiner/index.html.en).

2. private.xml 위치 찾기

3. 단순한 키 변환 문법(자세히:https://pqrs.org/osx/karabiner/xml.html.en#autogen-syntax)

키를 다른 키로 바꾸기: 

<autogen>__KeyToKey__ KeyCode::CONTROL_L, KeyCode::OPTION_L</autogen>

(왼쪽 CONTROL키를 왼쪽 ALT키로 바꾼다)

키를 다른 키 조합으로 바꾸기: 

<autogen>__KeyToKey__

    KeyCode::OPTION_R,

    KeyCode::SPACE, ModifierFlag::COMMAND_L

</autogen>

(오른쪽 OPTION키를 왼쪽 COMMAND + 스페이스 키로 바꾼다)

키 조합을 다른 키로 바꾸기: 

<!-- LCTRL+LSHIFT+ARROW(R/L) ==> VOLUME UP/DOWN -->

<autogen>__KeyToConsumer__

    KeyCode::CURSOR_RIGHT, ModifierFlag::CONTROL_L | ModifierFlag::SHIFT_L,

    ConsumerKeyCode::VOLUME_UP

</autogen>

<autogen>__KeyToConsumer__

    KeyCode::CURSOR_LEFT, ModifierFlag::CONTROL_L | ModifierFlag::SHIFT_L,

    ConsumerKeyCode::VOLUME_DOWN

</autogen>

  *3개 이상의 키 조합의 경우에는 pipe(|)를 이용한다.

키 조합을 다른 키 조합으로 바꾸기: 

<autogen>__KeyToKey__

    KeyCode::SPACE, ModifierFlag::SHIFT_L,

    KeyCode::SPACE, ModifierFlag::COMMAND_L

</autogen>


4. 디바이스 한정 문법

외장 키보드의 키 리맵이 내장 키보드에까지 영향을 주면 불편하기 때문에 키 리맵을 외장 키보드로 입력하는 경우만으로 제한하면 편리하다.

우선 메뉴 바의 사과마크를 클릭, "이 Mac에 관해서"를 선택한 다음, "시스템 리포트"를 눌러 외장 키보드를 찾는다.

<devicevendordef>

    <vendorname>LEOPOLD</vendorname>

    <vendorid>0x0853</vendorid>

</devicevendordef>

<deviceproductdef>

    <productname>FC660C</productname>

    <productid>0x0134</productid>

</deviceproductdef>

그 다음엔 위와 같이 디바이스와 제조사의 정보를 정의한 다음, 아이템에 device_only를 추가하면 된다.

<item>

        <name>FC660C</name>

        <identifier>private.fc660c</identifier>

        <device_only>DeviceVendor::LEOPOLD,DeviceProduct::FC660C</device_only>

 

        ... (키 리맵 정보들)


</item>

5. 예시

<?xml version="1.0"?>

<root>

 

    <devicevendordef>

        <vendorname>LEOPOLD</vendorname>

        <vendorid>0x0853</vendorid>

    </devicevendordef>

 

    <devicevendordef>

        <vendorname>APPLE</vendorname>

        <vendorid>0x05ac</vendorid>

    </devicevendordef>

 

    <deviceproductdef>

        <productname>FC660C</productname>

        <productid>0x0134</productid>

    </deviceproductdef>

 

    <deviceproductdef>

        <productname>AppleInternal</productname>

        <productid>0x0273</productid>

    </deviceproductdef>

 

    <item>

        <name>FC660C</name>

        <identifier>private.fc660c</identifier>

        <device_only>DeviceVendor::LEOPOLD,DeviceProduct::FC660C</device_only>

 

        <!-- CTRL+SHIFT+ARROW ==> VOLUME UP/DOWN -->

        <autogen>__KeyToConsumer__

            KeyCode::CURSOR_RIGHT, ModifierFlag::CONTROL_L | ModifierFlag::SHIFT_L,

            ConsumerKeyCode::VOLUME_UP

        </autogen>

 

        <autogen>__KeyToConsumer__

            KeyCode::CURSOR_LEFT, ModifierFlag::CONTROL_L | ModifierFlag::SHIFT_L,

            ConsumerKeyCode::VOLUME_DOWN

        </autogen>

 

        <autogen>__KeyToKey__

            KeyCode::OPTION_R,

            KeyCode::SPACE, ModifierFlag::COMMAND_L

        </autogen><!-- Right Option ==> Kor./Eng. Change -->

 

        <autogen>__KeyToKey__ KeyCode::CONTROL_L, KeyCode::OPTION_L</autogen>

        <autogen>__KeyToKey__ KeyCode::OPTION_L, KeyCode::COMMAND_L</autogen>

        <autogen>__KeyToKey__ KeyCode::COMMAND_L, KeyCode::CONTROL_L</autogen>

 

    </item>

 

    <item>

        <name>Apple::Alt.To.Lang.Chng</name>

        <identifier>private.AppleLangChng</identifier>

        <device_only>DeviceVendor::APPLE,DeviceProduct::AppleInternal</device_only>

        <autogen>__KeyToKey__

            KeyCode::OPTION_R,

            KeyCode::SPACE, ModifierFlag::COMMAND_L

        </autogen><!-- Right Option ==> Kor./Eng. Change -->

    </item>

 

</root> 

*여담인데, 문법이 include를 지원하기 때문에 이 설정 역시 dotfile과 함께 git으로 관리해도 편할 것 같다.

신고

심심해서 ncurses를 이용해서 팩맨을 짜봤다.
+ Genetic Programming으로 AI도 만들었다!

소스코드: http://github.lucent.me/pacman

신고