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

 

17406번: 배열 돌리기 4

크기가 N×M 크기인 배열 A가 있을때, 배열 A의 값은 각 행에 있는 모든 수의 합 중 최솟값을 의미한다. 배열 A가 아래와 같은 경우 1행의 합은 6, 2행의 합은 4, 3행의 합은 15이다. 따라서, 배열 A의 값은 4이다. 1 2 3 2 1 1 4 5 6 배열은 회전 연산을 수행할 수 있다. 회전 연산은 세 정수 (r, c, s)로 이루어져 있고, 가장 왼쪽 윗 칸이 (r-s, c-s), 가장 오른쪽 아랫 칸이 (r+s, c+s)인 정사각형을 시계

www.acmicpc.net

백준저지 17406번 문제 배열 돌리기4 문제 풀이입니다.

 

K가 6밖에 안되므로, 6개를 줄세우는 가짓수는 6!=720개 뿐입니다.

 

따라서 완전탐색을 하면 되는 구현위주의 문제입니다.

 

Rotation시키는 것은, 돌아가는 원판에 있는 숫자들을 다 저장해놓고, 한칸씩 더 이동시켜서 다시 넣는 방식으로 했으며

 

왼쪽위 오른쪽위, 오른쪽아래, 왼쪽아래 꼭지점을 기준으로

 

해당 꼭지점에 다다를때 이동방향을 바꾸는 식으로 구현해보았습니다.

 

 

#include <bits/stdc++.h>
using namespace std;

typedef pair<int, int> pii;
int A[55][55];
typedef struct _Rot {
	int r, c, s;
} Rot;
Rot rot[10];
int N, M, K;
int ans;
void get_input();
bool used[10];
int order[10];
void copy(int A[][55], int B[][55]);
const int dx[] = { +0, +1, +0, -1 };
const int dy[] = { +1, +0, -1, +0 };

void _rotate(int B[][55], int row, int col, int rad) {
	vector<int> val;
	int x = row - rad;
	int y = col - rad;
	int cnt = rad * 8;
	set<pii> check;
	check.insert(make_pair(row + rad, col + rad));
	check.insert(make_pair(row - rad, col + rad));
	check.insert(make_pair(row + rad, col - rad));
	int k = 0;
	for (int i = 0; i < cnt; i++) {
		val.push_back(B[x][y]);
		x += dx[k];
		y += dy[k];
		if (check.find(make_pair(x, y)) != check.end()) {
			k++;
		}
	}
	k = 0;
	x = row - rad;
	y = col - rad;
	x += dx[0];
	y += dy[0];
	for (int i = 0; i < cnt; i++) {
		B[x][y] = val[i];
		x += dx[k];
		y += dy[k];
		if (check.find(make_pair(x, y)) != check.end()) {
			k++;
		}
	}
}
void rotate(int B[][55], int row, int col, int rad) {
	for (int i = 1; i <= rad; i++) {
		_rotate(B, row - 1, col - 1, i);
	}
}
void dfs(int p) {
	if (p < K) {
		for (int i = 0; i < K; i++) {
			if (used[i] == false) {
				used[i] = true;
				order[p] = i;
				dfs(p + 1);
				used[i] = false;
			}
		}
	}
	else {
		//do simulation
		int B[55][55];
		copy(B, A);
		for (int i = 0; i < K; i++) {
			auto& o = rot[order[i]];
			rotate(B, o.r, o.c, o.s);
		}
		
		for (int i = 0; i < N; i++) {
			int sum = 0;
			for (int j = 0; j < M; j++) {
				sum += B[i][j];
			}
			ans = min(ans, sum);
		}

		
	}
}
int main() {
	ans = 987654321;
	get_input();
	dfs(0);
	cout << ans << endl;
}

void get_input() {
	cin >> N >> M >> K;
	for (int i = 0; i < N; i++) 
		for (int j = 0; j < M; j++) 
			cin >> A[i][j];
		
	for (int i = 0; i < K; i++) {
		auto& R = rot[i];
		cin >> R.r >> R.c >> R.s;
	}
}
void copy(int A[][55], int B[][55]) {
	for (int i = 0; i < N; i++)
		for (int j = 0; j < M; j++) {
			A[i][j] = B[i][j];
		}
}

Author's Official blog url: http://trapkit.de/tools/checksec.html

Official checksec.sh download url: http://trapkit.de/tools/checksec.sh

 

위 파일이 checksec.sh 공식 파일이긴한데, 리눅스에서 다운받아 사용 시 아래와 같은 에러가 나는 경우가 있다.

 

bash: /usr/bin/checksec.sh: /bin/bash^M: bad interpreter: No such file or directory.

 

 

그러면 Unix에 맞는 형식으로 저장을 하면 되긴하는데, 여러모로 귀찮으므로 아래 링크된 파일을 다운받아 쓰세요..!

 

 

 

checksec.sh Linux version

checksec.sh
0.03MB

 

 

 

 

 

Caesar Cipher Text En/Decoder

Input



Output

Policy

  • Shift applied for only English alphabet.
  • Other characters are remained unchanged.
  • 영문자들만 쉬프트가 적용된다.
  • 그 외 문자들은 변경되지 않는다.

'해킹 & 보안' 카테고리의 다른 글

워게임 사이트 root-me.org  (0) 2019.11.27
checksec.sh Linux ver  (0) 2019.07.25
[HackCTF] Web - Home 풀이  (0) 2019.07.16
[HackCTF] Web - Guess me 풀이  (0) 2019.07.16
[HackCTF] Web - Hidden 풀이  (0) 2019.07.16

이제 거의 막바지에 다다랐다.

 

지속적으로 스트리밍을 하도록 하려면 m3u8파일이 계속해서 바뀌게 되는데, 이거를 위해서 manifest 파일도 계층구조를 좀 가져야 했다.

 

playlist라는 manifest가 기본적으로 있고 이녀석은 chunkData라는 녀석을 보라고 한다.

 

그리고 chunkData는 스트리밍이 됨에 따라서 ts파일이 계속 슬라이딩 윈도우 마냥 진행되면, HLS 클라이언트는

 

이를 알고 알아서 지속으로 chunkData를 업데이트 하면서 ts파일들을 버퍼링해서 재생을 시킨다.

 

 

요녀석을 구현하기 위해서 했던 짓들을 커밋 이력을 보면서 다시 복습을 했는데, 이걸 잘 몰라서 삽질을 한번 했던 것 같다.

 

m3u8라는 Nodejs 모듈에서 m3u8 파싱 기능을 제공한다. 이를 이용해서 직접 m3u8 파일을 한땀한땀 변경하면서 작업을 했던 것 같은데, 아마 ffmpeg에서 이것 또한 잘 해주는 그런 기능이 있었던 것 같다.

 

 

그리고 나서 테스트를 했다가, 클라이언트 단에서 재생이 자꾸 끊기는 현상이 있었는데,

 

이유를 당최 몰라서 해메다가 hls-client javascript 코드를 한번 까보면서 알게 되었다.

 

기승전 코드까보기..ㅠㅠ

 

코드중에 logger와 같은 객체가 있었는데 안에 모두 빈 함수가 있었었다.

 

그 함수들을 console.log와 같은 함수로 바꿔치기를 한 뒤 실행을 해 보니, 브라우저에서 콘솔로 warning들이 떳었다.

 

 

 

그 원인은 즉슨, 클라이언트에서 몇초마다 새로운 음악파일을 서버로 보내고, 서버에서는 각각을 ffmpeg으로 가공을 했다.

 

그런데 다시 hls client에서는 다른 파일에 대한  ffmpeg-ts가 들어오니, 음악의 흐름이 끊긴줄 알게 된 것이다.

 

 

그래서 이러한 현상을 해결하기위해서 ffmpeg에서 가공할 때 omit_endlist 라는 옵션(오래되서 가물가물하긴 한데 아마 맞을 것이다)을 추가해서 이러한 현상을 해결했던 것 같다.

 

 

어쨋든 이런식으로 해결을 하다보니 지금 상태의 코드가 되었다.

 

음질이라던지 내부적으로 부족한점이 매우 많지만, 그래도 어쨋든 대충이라도 동작은 하기는 했다.

 

중간에 삽질도 많았지만 여러모로 배우기도 했다.

 

더 이상 까먹기 전에 블로그에 정리를 했으니, 이 내용은 이만 당분간 묻어두도록 하겠다.

 

 

최종 코드 스냅샷이다.

 

https://github.com/Einstrasse/hls-service/tree/d92a3f6abb378cf0947c3ba928f9d8b57d3a8118

 

Einstrasse/hls-service

Nodejs base hls server. Contribute to Einstrasse/hls-service development by creating an account on GitHub.

github.com

 

이제 클라이언트에서 녹음한 파일이 서버로 전송이 되었으니, 이를 잘 가공해서 스트리밍 할 수 있도록 해 보아야 한다.

 

음성 파일을 ts 파일(Transport stream)으로 나누고, 그 리스트들을 가지고 있는 m3u8 manifest 파일을 생성해야 한다.

 

 

일단 음성 파일을 MPEG-TS 파일로 변경하는 것은 내 개인 능력으로는 터무니 없이 불가능하다는 생각이 드는 것은 자명하다(?)

 

따라서 무조건 ffmpeg 코덱을 이용해서 변환을 해야 하는데, m3u8파일은 포맷에 맞춰서 직접 만들어야 할 줄 알았다.

 

왜냐면 text파일 형식에 간단해보였기에... 하지만 ffmpeg에서 m3u8 파일 역시 생성해주는 옵션들이 있었다.

 

 

Nodejs 모듈 중에서 fluent-ffmpeg이라는 모듈이 있었다.

https://github.com/fluent-ffmpeg/node-fluent-ffmpeg

 

fluent-ffmpeg/node-fluent-ffmpeg

A fluent API to FFMPEG (http://www.ffmpeg.org). Contribute to fluent-ffmpeg/node-fluent-ffmpeg development by creating an account on GitHub.

github.com

 

맨 처음 요녀석을 보고 그냥 ffmpeg을 nodejs로 포팅한 대단한 녀석이라고 생각했다.

 

ffmpeg은 딱봐도 계산량이 많아서 C나 C++등으로 작성된 네이티브 바이너리일 것이고, Nodejs는 Javascript 런타임인데, Javascript로 코덱을 만든다라 대단하다 생각했는데

 

사실 fluent-ffmpeg 이녀석은 실제 ffmpeg 바이너리를 가지고 있을 때, ffmpeg의 수많은 인자들을 javascript API로 바꾸어주는 녀석정도 밖에 안되는 녀석이었다. ffmpeg 실행 커맨드라인 래퍼(wrapper) 정도 인 녀석이다.

 

그래도뭐 chile_process.exec("ffmpeg -args"); 요런식으로 하는 것 보다는 좀 나으니, 사용해보도록 했다.

 

여기 까지 작성해본 코드 스냅샷이다.

 

https://github.com/Einstrasse/hls-service/tree/2d41cf29fa853142e891d4cbedb638e9b66b3ba7

 

Einstrasse/hls-service

Nodejs base hls server. Contribute to Einstrasse/hls-service development by creating an account on GitHub.

github.com

 

그리고 클라이언트에서 음성 파일들을 보내는데, 이전에는 길게 녹음해서 한번 큰 덩어리를 보내는 식이었는데,

 

라이브 스트리밍이라는 컨샙을 맞추기 위해서, 짧은 시간 동안 녹음한 내용을 주기적으로 서버로 보내도록 코드를 변경해 보았다.

 

여기까지 변경한 코드 스냅샷 부분이다.

 

https://github.com/Einstrasse/hls-service/tree/d55e4a6cb72f7d59917b4d62905b43db31f29039

 

Einstrasse/hls-service

Nodejs base hls server. Contribute to Einstrasse/hls-service development by creating an account on GitHub.

github.com

 

HLS를 이용한 라이브 라디오 방송 웹 앱 개발기 (2) 를 작성한지 꽤 오랜 시간만에 3편을 작성해본다.

 

나중에 써야지 써야지 하다가 귀찮아서 작성하지 않고 있었는데, 더이상 까먹기 전에 이어서 작성을 해보려고 한다.

 

2편까지에서는 브라우저단에서 마이크 API를 이용해서 음성 파일을 저장하는 것 까지 했었다.

 

이제 이 음성 데이터를 서버로 전송을 시켜야 한다.

 

 

일단 파일형태로 전송을 하는 것을 생각을 했는데, 웹에서 파일 업로드시 많이 사용하는 방식이 POST 메소드로 form으로 데이터를 날리는 방식이다.

 

이때 인코딩 타입은 multipart/form 형식으로 보낸다. 바이너리 값 그대로 HTTP 패킷에 담아서 보내는 방식이다.

 

Nodejs에서 multipart/form-data를 받는 부분을 작성을 하려고 했는데 Nodejs에서 해당 데이터를 받는 것은

 

Express.js 프레임워크에서 Multer라는 미들웨어를 사용하는 방식밖에는 잘 몰랐다.

 

하지만 나는 가볍게 만들어볼 생각에 Express.js 프레임워크는 사용하고 싶지 않았던 탓에, multer말고 multipart/form 을 파싱할 수 있는 nodejs package를 찾아야 했다.

 

 

그래서 가장 간단한 방법 중 하나인, multer의 코드를 까 보았다..!

 

https://github.com/expressjs/multer/blob/master/doc/README-ko.md

 

expressjs/multer

Node.js middleware for handling `multipart/form-data`. - expressjs/multer

github.com

 

지금 위 링크로 가 보니, 내부적으로 busboy를 사용한다고 대놓고 나와있다. 작년에 프로젝트 작업을 할 때에는 코드를 까서 알아냈었는데...

 

어쨋든 busboy란 녀석을 multer가 내부적으로 사용한다는 것을 알 수 있다.

 

https://github.com/mscdex/busboy

 

mscdex/busboy

A streaming parser for HTML form data for node.js. Contribute to mscdex/busboy development by creating an account on GitHub.

github.com

 

busboy 문서를 보면 html form data를 파싱할 수 있다는 것을 알 수 있다.

 

어쨋든 이녀석을 이용해서 서버단을 작성해준다.

 

 

그리고 클라이언트 단에서 multipart/form-data 인코딩 방식으로 된 form data를 서버로 보내는 것도 해 주어야 하는데,

 

요녀석은 XHR(XMLHttpRequest)를 이용해서 간단하게 작성해 보았다.

 

처음에는 이녀석을 jQuery 라이브러리를 통해서 접했는데, Low하게는 브라우저단에 구현된 XHR 객체에서 서버와의 동적 비동기 통신이 가능하다는 것을 나중에 알게 되었다.

 

여기까지 작성해본 코드 스냅샷 링크이다.

 

https://github.com/Einstrasse/hls-service/tree/b52043c77ec377b70476619556d3a6e187823cb3

http://ctf.j0n9hyun.xyz:2034/

 

http://ctf.j0n9hyun.xyz:2034/

 

ctf.j0n9hyun.xyz:2034

Home이라는 이름의 문제이다.

 

대충 요런 페이지인데, 뭐 아무것도없다.

 

소스를 봐도

별게 없다.

 

힌트로는 "머리말" 이라는데, 조금 고민을 해 보았다.

 

 

머리말->머리 ->Head -> Header??

 

HTTP Header와 관련된 것이라는 생각이 좀 들었다.

 

버프슈트로 한번 패킷을 잡아보았는데 별게 없었다.

 

그래서 검색 신공!!

 

 

X-Forwarded-For라는 헤더가 있다고 한다. 프록시를 사용할때 원래 아이피를 기록하는 그런 헤더인데,

 

이녀석을 버프슈트에다가 박아서 요청을 한번 때려보았다.

와우

구웃. 풀엇다 ㅎㅎ!!

'해킹 & 보안' 카테고리의 다른 글

checksec.sh Linux ver  (0) 2019.07.25
Caesar Cipher Text Online Codec (Batch)  (0) 2019.07.25
[HackCTF] Web - Guess me 풀이  (0) 2019.07.16
[HackCTF] Web - Hidden 풀이  (0) 2019.07.16
버프슈트 설정하기 (Burp suite configuration)  (0) 2019.06.18

https://ctf.j0n9hyun.xyz/challenges#Guess%20me

100점짜리 웹 문제다.

 

딱 보이는건 extract($_GET)

 

GET query parameter로 저기 있는 변수들을 poisoning할 수 있다. 원하는 값으로 바꿀수 있단 뜻.

 

$filename 을 원하는 값으로 바꿔서 $secretcode를 원하는 값으로 바꾼 뒤, $guess랑 똑같게 만들고 싶은데, 

 

저 서버에 있는 파일들 중 존재하는 녀석의 파일 명과, 그 내용을 알아야한다. 따라서 좀 고민하던 차에

 

php file_get_contents 함수에 대해 한번 검색해보았다.

 

http://docs.php.net/manual/kr/function.file-get-contents.php

 

PHP: file_get_contents - Manual

file_get_contents (PHP 4 >= 4.3.0, PHP 5, PHP 7) file_get_contents — Reads entire file into a string 설명 string file_get_contents ( string $filename [, bool $use_include_path = false [, resource $context [, int $offset = 0 [, int $maxlen ]]]] ) file_get_con

docs.php.net

설명을 보는데, 이게 웬걸? 웹 리소스도 가져올 수 있는 것 처럼 보인다. ㅎㄷㄷ!!

그래서 바로 goorm IDE를 켜서 퍼블릭 도메인에 서버를 하나 띄웠다.

 

 

그리고

 

URL을 구성해서 요청하니 flag가 나온다. 하핫

HackCTF URL

https://ctf.j0n9hyun.xyz

 

HackCTF

Do you wanna be a God? If so, Challenge!

ctf.j0n9hyun.xyz

 

웹 문제, Hidden을 풀어보겠다.

URL로 접속해보자

 

버튼이 1~4까지 있다. 소스를 한번 보자

 

 

get으로 form을 보내는 방식이다.

 

http://ctf.j0n9hyun.xyz:2023/?id=5 

 

위와같이 요청을 보내보자. 

 

flag를 바로 알 수 있다.

 

50점짜리 답게 간단한 문제였다.

https://atcoder.jp/contests/abc131

https://img.atcoder.jp/abc131/editorial.pdf

 

 

앳코더 비기너 컨테스트 131 문제들 풀이입니다.

 

오랜만에 PS 문제좀 풀어볼려고 앳코더에서 비교적 난이도가 쉬운 beginner contest 를 하나 골라서 업솔빙 해보았습니다.

 

원래 컨테스트일때는 참여 자체를 안하고, 남는 시간에 하나하나 풀어보았는데, D번까지는 쉽게 풀었고, E번부터는 힌트가 꽤 필요했습니다.

 

Editorial을 찾아보았는데, 일본어 버전만 제공되는 것 같아서 한국어로 풀이를 작성해보도록 하겠습니다.

 

https://atcoder.jp/contests/abc131/tasks/abc131_a

 

A - Security

AtCoder is a programming contest site for anyone from beginners to experts. We hold weekly programming contests online.

atcoder.jp

A번 문제입니다. 인접한 녀석이 같은 값이 있는지 아닌지만 확인하면 되므로, 매우 간단한 문제입니다. 그냥 그대로 구현하면 됩니다.

 

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
int main() {
	string s;
	cin >> s;
	char p = s[0];
	for (size_t i = 1; i < s.length(); i++) {
		if (s[i] == p) {
			cout << "Bad";
			return 0;
		}
		p = s[i];
	}
	cout << "Good";
}

 

https://atcoder.jp/contests/abc131/tasks/abc131_b

 

B - Bite Eating

AtCoder is a programming contest site for anyone from beginners to experts. We hold weekly programming contests online.

atcoder.jp

B번 문제입니다. 사과의 풍미(flavor) 값은 연속된 수열 값으로 나타납니다. 전체 값 중에서 사과 하나를 뺏을 때 차이가 가장 적을려면, 전체 값 중에 절대값이 가장 작은 것을 하나 빼버리면 됩니다.

O(N)으로 해결할 수 있습니다.

 

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
int main() {
	int N, L;
	cin >> N >> L;
	int S = (N - 1) * N / 2 + N * L;
	int dect = 987654321;
	for (int i = 0; i < N; i++) {
		int cand = i + L;
		if (abs(cand) < abs(dect)) {
			dect = cand;
		}
	}
	cout << (S - dect) << endl;
}

https://atcoder.jp/contests/abc131/tasks/abc131_c

 

C - Anti-Division

AtCoder is a programming contest site for anyone from beginners to experts. We hold weekly programming contests online.

atcoder.jp

C번 문제입니다. 포함배제 Counting 문제입니다.

 

값 C로도 나누어 떨어지지 않고 D로도 나누어 떨어지지 않는 수의 개수는

(전체) - (C로 나누어 떨어지는 수) - (D로 나누어 떨어지는 수) + (C와 D 둘다 나누어 떨어지는 수)

가 됩니다.

 

C와 D 둘다 나누어 떨어지는 수는 C와 D의 LCM(최소공배수)으로 나누어 떨어지는 수와 같습니다.

C와 D의 최소 공배수는 C*D / gcd(C, D)와 같습니다.

gcd(C, D)는 C와 D의 최대공약수를 뜻합니다.

#include <bits/stdc++.h>
#include <algorithm>
using namespace std;
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
template<class T>
T gcd(T a, T b)
{
	if (b == 0) return a;
	a %= b;
	return gcd(b, a);
}
int main() {
	ll A, B, C, D;
	cin >> A >> B >> C >> D;
	ll total = B - A + 1;
	ll Cdiv = B / C - ((A - 1) / C);
	ll Ddiv = B / D - ((A - 1) / D);
	ll LCM = C * D / gcd(C, D);
	ll CDdiv = B / LCM - ((A - 1) / LCM);
	ll ans = total - Cdiv - Ddiv + CDdiv;
	cout << ans << endl;
}

https://atcoder.jp/contests/abc131/tasks/abc131_d

 

D - Megalomania

AtCoder is a programming contest site for anyone from beginners to experts. We hold weekly programming contests online.

atcoder.jp

D번 문제입니다. 모든 일을 다 끝낼 수 있는지 아닌지만 알아내면 됩니다.

단순한 그리디 문제입니다. 데드라인이 앞에 있는, 녀석들을 먼저 끝내는 식으로 진행을 하고, 그 중간에 데드라인을 넘어가는 녀석이 생기면 실패, 아니면 성공입니다.

 

이를 어떻게 알 수 잇냐면, 데드라인이 빠른녀석을 먼저 처리하지 않는 경우를 고려했을때,

데드라인이 빠른 녀석을 먼저 처리하는 경우로 변형이 가능하고, 변형을 했을 때 변형하기 전 보다 무조건 상태가 비슷하거나 나아진다는 것을 증명하면 됩니다.

 

데드라인이 빠른 순서대로 sorting이 되어 있는 상태라고 가정해 봅시다.

뭐 다음과 같이 있겠죠

 

$$b_{1},\space b_{2},\space b_{3},\space b_{4},\space b_{5},\space b_{6}\space ... \space b_{n-1},\space b_{n}$$

데드라인이 더 느린 녀석을 먼저 하나 처리를 했는데도 모두 처리를 가능한 상황이라고 가정해 보겠습니다.

$$b_{1},\space b_{2},\space b_{6},\space b_{4},\space b_{5},\space b_{3}\space ... \space b_{n-1},\space b_{n}$$

6번이랑 3번이 순서가 바뀌어서 처리 되어도 모두 처리가능한 상황이라는 것이죠.

 

여기서 다시 원래 순서, 1,2,3,... 순서로 바꾼다고 가정해봅시다. 3과 6번을 다시 순서를 바꾸는거죠

 

일단 바뀌는 두 당사자 뒤에 처리되는 녀석들은, 즉 6번 이후 녀석들은 처리되는 시기가 변화가 없으므로 상관이 없습니다. 처리되는 시기는 1번 부터 6번의 처리시간을 다 합친 이후에 처리가 될 것이므로 변화가 없습니다.

 

데드라인이 빠른 녀석(3)은 늦게 처리되어도 되던 상황에서 더 빨리 처리가 되었고,

그래서 그 녀석 때문에 문제가 될 일은 없습니다.

 

데드라인이 느린 녀석(6)은 더 뒤에 처리되게 되었는데, 자신보다 데드라인이 빠른 녀석도 처리가 되었던 위치이므로, 문제없이 처리가 되게 됩니다.

 

따라서 둘의 위치를 바꾸더라도 문제없이 처리가 가능합니다.

 

두번째 경우는 아래 순서로 처리할 경우 실패하는 경우입니다.

$$b_{1},\space b_{2},\space b_{6},\space b_{4},\space b_{5},\space b_{3}\space ... \space b_{n-1},\space b_{n}$$

위 순서대로 처리를 하면 실패를 합니다.

다시 그래서 3번과 6번을 바꾸어서 처리한다고 봅시다.

마찬가지로 6번 이후 녀석들은 처리 시기가 바뀌지 않아서 신경 쓸 필요가 없습니다. 3번 이전 녀석도 마찬가지죠.

3번이 앞으로 오게 되는데, 3번이 앞으로 오면서 여기서 만약 데드라인이 걸린다고 칩시다.

그런데 기존에는 6번이 3번째 자리에 있었는데, 6번의 데드라인이 3번 데드라인보다 길기 때문에 3번이 앞으로 와서 데드라인이 걸렸다면, 6번 자리에 있었을 경우에도 무조건 데드라인이 걸렸을 것입니다.

따라서 3번이 바뀐 자리때문에 걸렸다면 어차피 걸릴 상황이란거죠. 따라서 밑져야 본전인 상황입니다.

 

6번이 뒤로 가면서 걸렸다면 3번이 뒤에 있었을 때도 걸렸을 것입니다. 3번의 데드라인이 더 짧거든요. 이 경우에도 밑져야 본전입니다.

 

그리고 바꾸기 전에는 3번이 6번자리에서 데드라인에 걸리지만, 바꾸고 나서는 안걸리는 케이스가 있을 수 있습니다. 따라서 이 경우에는 기존에는 실패지만, 바꾸고 나서 성공이 되므로 좀 더 나은 경우이죠.

 

약간 증명이 중구난방이긴한데 어쨋든, 이런식으로 증명이 가능합니다.

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
 
int main() {
	int n;
	cin >> n;
	vector<pii> a(n); // deadline, duration
	for (int i = 0; i < n; i++) {
		cin >> a[i].second >> a[i].first;
	}
	sort(a.begin(), a.end());
	int elap = 0;
	for (int i = 0; i < n; i++) {
		elap += a[i].second;
		if (elap > a[i].first) {
			cout << "No";
			return 0;
		}
	}
	cout << "Yes";
	return 0;
}

 

 

 

https://atcoder.jp/contests/abc131/tasks/abc131_e

 

E - Friendships

AtCoder is a programming contest site for anyone from beginners to experts. We hold weekly programming contests online.

atcoder.jp

E번 문제입니다. 문제 내용을 잘못 이해해서 좀 해맸었는데, 일단 N개의 vertex로 shortest path = 2인 pair를 최대한 많이 만드는 구성을 한번 생각해봤습니다.

 

N=7이라 한다면, 대충 위와 같이 생겼을 때 가장 많은 shortest path = 2인 쌍이 생깁니다. 1을 제외한 모든 조합들이 가능한거죠.

즉 //(\dbinom{6}{2}//)개 만큼 생깁니다. 

 

N에 대하여 일반화를 하면 //(\dbinom{N-1}{2}//)가지가 나오는 것이죠. 요렇게 나오는 경우가 최대 경우입니다.

 

따라서 //(K > \dbinom{N-1}{2} //)이면 불가능한 경우입니다.

 

그렇다면 저 최대값 이하의 값은 다 만들수가 있느냐? 

 

이에 대한 답은 Yes입니다. 위 그림에서 예를 들어 4와 5를 연결해버리면, 4에서 5로 가는 최단거리가 1이 되어서, 만족하는 쌍의 수가 하나 줄게 됩니다. 그리고 그 추가된 Edge는 다른 pair들에는 전혀 영향을 끼치지 않죠. 이런식으로 하나하나씩 줄여서 K값을 0으로 만들수도 있습니다.

 

따라서 N값에 따라서 0~최대값까지 죄다 만들 수 있는 것이죠. 이 범위 안에 들어있다면 저런 형태의 graph를 리턴하고, 그렇지 않다면 불가능하다라고 출력하면 됩니다.

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
int main() {
	int N, K;
	cin >> N >> K;
	int maxi = (N - 1)*(N - 2) / 2;
	if (K > maxi) {
		cout << -1;
		return 0;
	}
	int M = N - 1;
	int diff = maxi - K;
	cout << (M + diff) << endl;
	for (int i = 0; i < M; i++) {
		cout << 1 << ' ' << i + 2 << endl;
	}
	int src = 2;
	int dst = 3;
	for (int i = 0; i < diff; i++) {
		cout << src << ' ' << dst << endl;
		dst++;
		if (dst == N + 1) {
			src++;
			dst = src + 1;
		}
	}
}

 

https://atcoder.jp/contests/abc131/tasks/abc131_f

 

F - Must Be Rectangular!

AtCoder is a programming contest site for anyone from beginners to experts. We hold weekly programming contests online.

atcoder.jp

F번입니다. 꽤나 어려웠는데요. 규칙을 찾아내면 생각보다는 간단합니다.

 

코드포스에서 풀이 올리신분들의 이야기를 보면 대충 다음과 같은 식으로 풀이합니다.

 

1. 점들을 graph로 모델링한다

2. 각각의 점은 vertex

3. 각각의 점과 같은 x좌표 혹은 y좌표에 있는 점 사이에 edge가 있다고 친다.

4. component들을 dfs혹은 bfs로 찾아낸다.

5. 각각 component에서 서로 다른 x좌표 개수와 y좌표 개수를 샌 뒤 그 둘을 곱한 개수를 모두 더한다.

6. 모두 더한 그 수가, 해당 operation을 최대한 사용해서 만들 수 있는 점들의 개수이다.

7. 여기서 원래 있던 점 수를 빼면 정답

 

요녀석이 왜 되는지 한번 생각을 해 보도록 합시다. 직접 종이에 그려보다 보면 대충 규칙성이 보입니다.

 

같은 x좌표, y좌표에 2개 이상의 점이 있으면, operation으로 한 점을 더 만들고, 그녀석으로 또 만들고 이러한 반복 작업을 통해서 바둑판식으로 점들을 모두 매워버릴 수 있습니다.

 

이런 성질을 관찰함으로써 위와 같은 풀이가 나오게 됩니다.

 

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
#define MAXN 100005
bool chkX[MAXN], chkY[MAXN], visit[MAXN];
vector<int> xline[MAXN], yline[MAXN];
int X[MAXN], Y[MAXN];
int main() {
	int N;
	cin >> N;
	ll ans = -N;
 
	for (int i = 0; i < N; i++) {
		int x, y;
		cin >> x >> y;
		X[i] = x;
		Y[i] = y;
		xline[x].push_back(i);
		yline[y].push_back(i);
	}
 
	for (int i = 0; i < N; i++) {
		if (visit[i]) continue;
		visit[i] = true;
		set<int> xdist, ydist;
		xdist.clear();
		ydist.clear();
 
		queue<int> q;
		q.push(i);
		while (q.size()) {
			int c = q.front(); q.pop();
			int x = X[c];
			int y = Y[c];
			xdist.insert(x);
			ydist.insert(y);
 
			if (chkX[x] == false) {
				chkX[x] = true;
				for (auto nx : xline[x]) {
					if (visit[nx] == false) {
						visit[nx] = true;
						q.push(nx);
					}
				}
			}
 
			if (chkY[y] == false) {
				chkY[y] = true;
				for (auto ny : yline[y]) {
					if (visit[ny] == false) {
						visit[ny] = true;
						q.push(ny);
					}
				}
			}
		}
 
		ans += (ll)xdist.size() * (ll)ydist.size();
	}
 
	cout << ans << endl;
	return 0;
 
}

+ Recent posts