ABC 190 간단 후기 올립니다~ (atcoder.jp/contests/abc190)

A - Very Very Primitive Game

간단한 구현 문제입니다. 먼저 시작하는 사람의 캔디가 더 많으면 먼저 시작하는 사람이 이기고, 그렇지 않으면 다른 사람이 이깁니다.

int main() {
	int a, b, c; cin >> a >> b >> c;
	if (c == 0) {
		if (a > b) cout << "Takahashi";
		else cout << "Aoki";
	}
	else {
		if (b > a) cout << "Aoki"; 
		else cout << "Takahashi";
	}
}

 

B - Magic 3

간단한 구현 문제입니다.  X < S and Y > D인 (X, Y)가 존재하면 Yes, 아니면 No를 출력하면 됩니다.

int main() {
	int n, s, d; cin >> n >> s >> d;
	int x, y;
	while (n--) {
		cin >> x >> y;
		if (x<s && y>d) {
			cout << "Yes"; return 0;
		}
	}
	cout << "No";
}

 

C - Bowls and Dishes

완전탐색 문제입니다. K명의 사람이 C or D를 선택하는 모든 경우(2^K)에 대하여 가장 많은 조건을 만족하는 경우를 출력하면 됩니다

typedef pair<int, int> p;
 
int N, M, K, ans, S[100];
p ab[100], cd[100];
 
void f(int idx) {
	if (idx == K) {
		int dish[104], i;
		for (i = 1; i <= N; i++) dish[i] = 0;
		for (i = 0; i < K; i++) {
			if (S[i] == 1) dish[cd[i].x] = 1;
			else dish[cd[i].y] = 1;
		}
		int sum = 0;
		for (i = 0; i < M; i++) {
			if (dish[ab[i].x] && dish[ab[i].y]) sum++;
		}
		ans = max(ans, sum);
		return;
	}
	S[idx] = 1; f(idx + 1);
	S[idx] = 2; f(idx + 1);
}
 
int main() {
	int i;
	cin >> N >> M;
	for (i = 0; i < M; i++) cin >> ab[i].x >> ab[i].y;
	cin >> K;
	for (i = 0; i < K; i++) cin >> cd[i].x >> cd[i].y;
	f(0);
	cout << ans;
}

 

D - Staircase Sequences

수학 문제입니다. 등차수열의 시작과 끝을 각각 a, b라고 할 경우 a + (a+1) + ... + b = N 인 (a, b) 쌍의 개수를 구하면 됩니다. 좌변은 (1+2+...+b) - (1+2+...+a-1) 이므로, b(b+1) - a(a-1)= 2N --> (b+a)(b-a+1)=2N이 됩니다.

2N을 xy꼴로 나타내서 x=b+a, y=b-a+1 or x=b-a+1, y=b+a가 성립하는지 확인하면 됩니다.

ll N;

inline int f(ll x, ll y) {
	if ((x + y - 1) % 2 == 0) return 1;
	return 0;
}
 
int main() {
	cin >> N;
	N *= 2;
	ll ans = 0;
	for (ll i = 1; i*i <= N; i++) {
		if (N%i) continue;
		ll j = N / i;
		if (f(i, j)) ans++;
		if (f(-i, -j)) ans++;
	}
	cout << ans;
}

 

E - Magical Ornament

Graph + bit DP 문제입니다. M개의 이웃한 쌍 (A,B)를 edge, N개의 gem이 node인 graph를 생각하자. K개의 C에 대하여 Ci에서 Cj의 shortest path를 구하자.(BFS 이용) 모든 (Ci, Cj) 쌍의 shortest path를 D[K][K]라고 하자. K개의 C를 node, D를 edge로 갖는 Graph에서 임의의 정점에서 다른 모든 노드를 방문하는 최소 비용 (salesman problem)을 구하면 된다.

int N, M, K, C[20], CC[100004], CI[100004], S[20];
vector<int> adj[100004];
int adj2[20][20];
const int INF = (int)1e9;
int dp[1 << 17][17];
 
int dfs(int state, int u) {
	int &ret = dp[state][u]; if (ret != -1) return ret;
	if (state == ((1 << K) - 1)) return ret = 0;
 
	ret = INF;
	for (int v = 0; v < K; v++) {
		if (state&(1 << v)) continue;
		if (adj2[u][v] == -1) continue;
		int a = dfs(state | (1 << v), v) + adj2[u][v];
		if (a < INF) ret = min(ret, a);
	}
	return ret;
}
 
int main() {
	int i, j;
	cin >> N >> M;
	for (i = 0; i < M; i++) {
		int a, b; cin >> a >> b;
		adj[a].push_back(b); adj[b].push_back(a);
	}
	cin >> K;
	for (i = 0; i < K; i++) {
		cin >> C[i]; CC[C[i]] = 1; CI[C[i]] = i;
	}
 
	for (i = 1; i <= N; i++) {
		if (CC[i] == 0) continue;
		queue<int> q; q.push(i);
		vector<int> dist(N + 1, -1); dist[i] = 0;
		while (!q.empty()) {
			int u = q.front(); q.pop();
			for (auto v : adj[u]) {
				if (dist[v] != -1) continue;
				dist[v] = dist[u] + 1;
				q.push(v);
			}
		}
		for (j = 1; j <= N; j++) {
			if (CC[j] == 0 || i == j) continue;
			adj2[CI[i]][CI[j]] = adj2[CI[j]][CI[i]] = dist[j];
		}
	}
 
	int ans = INF;
	for (i = 0; i < (1 << K); i++) for (j = 0; j < K; j++) dp[i][j] = -1;
	for (i = 0; i < K; i++) {
		int ret = dfs(1 << i, i);
		if (ret < INF) ans = min(ans, ret + 1);
	}
	if (ans == INF) ans = -1;
	cout << ans;
}

 

F - Shift and Inversions

Segment Tree 문제입니다. i ~ i + N -1 구간의 inversion count를 구하면 됩니다. (0<= i <N) i=0일 경우는 segment tree를 이용하면 됩니다. (소스코드 rev_cnt) i=1 ~ N-1일 경우는 A[i]보다 작은 숫자와 큰 숫자는 O(1)에 찾을 수 있습니다. (소스크도 up, dn) 이유는 0 ~ N-1 숫자가 한 번씩 나오기 때문입니다.

struct sg {
	int st[1200004];
	void upd(int a, int b, int node, int node_st, int node_en) {
		if (a < node_st || node_en < a) return;
		if (node_st == node_en) {
			st[node] = b; return;
		}
		upd(a, b, node * 2, node_st, (node_st + node_en) / 2);
		upd(a, b, node * 2 + 1, (node_st + node_en) / 2 + 1, node_en);
		st[node] = st[node * 2] + st[node * 2 + 1];
	}
	int qry(int a, int b, int node, int node_st, int node_en) {
		if (a > node_en || node_st > b) return 0;
		if (a <= node_st && node_en <= b) return st[node];
		return qry(a, b, node * 2, node_st, (node_st + node_en) / 2) +
			qry(a, b, node * 2 + 1, (node_st + node_en) / 2 + 1, node_en);
	}
}S;
 
int main() {
	int i, n; cin >> n;
	vector<int> A(n + 1);
	ll rev_cnt = 0;
	for (i = 1; i <= n; i++) {
		cin >> A[i]; A[i]++;
		S.upd(A[i], 1, 1, 1, n);
		rev_cnt += (i - S.qry(1, A[i], 1, 1, n));
	}
	cout << rev_cnt << '\n';
	for (i = 1; i < n; i++) {
		int dn, up;
		dn = A[i] - 1; up = n - A[i];
		rev_cnt = rev_cnt - dn + up;
		cout << rev_cnt << '\n';
	}
}

'코딩 > 코딩(코딩 문제, 알고리즘 등등)' 카테고리의 다른 글

백준 1001(A-B)  (0) 2021.09.08
백준 1000(A+B)  (0) 2021.09.08
백준 3028(창영마을)  (0) 2021.09.05
AtCoder Beginner Contest 189 후기  (0) 2021.01.26
Google Code jam Round 1C 2019 후기  (0) 2019.05.06
Google Code jam Round 1B 2019 후기  (0) 2019.04.30

ABC 189 간단 후기 올립니다~ (atcoder.jp/contests/abc189)

A - Slot

간단한 구현 문제입니다.

int main() {
	string s; cin >> s;
	if (s[0] == s[1] && s[0] == s[2]) cout << "Won";
	else cout << "Lost";
}

 

B - Alcoholic

1번 부터 순차적으로 마시면서 alcohol이 X가 넘는 순간을 찾으면 됩니다. x에 100을 곱해서 소수점 계산을 피했습니다.

int main() {
	int n, x; cin >> n >> x; x *= 100;
	int sum = 0;
	for (int i = 1; i <= n; i++) {
		int a, b;
		cin >> a >> b;
		sum += a * b;
		if (sum > x) {
			cout << i; return 0;
		}
	}
	cout << -1;
}

 

C - Mandarin Orange

모든 l, r에 대한 A[l..r] 최소값 * (r-l+1) 중 최대값을 출력하면 됩니다.

(l, r) 순서쌍이 O(N^2), A[l..r] 최소값은 스위핑을 이용하여 O(1)에 구할수 있습니다. (전체 시간 복잡도 : O(N^2))

참고로, 분할정복으로 O(NlogN) , stack을 이용하여 O(N) 으로 구할 수 있을거 같습니다.

ll x[10004];
int main() {
	int i, j, n; cin >> n;
	ll ans = 0, mn;
	for (i = 1; i <= n; i++) cin >> x[i];
	for (i = 1; i <= n; i++) {
		mn = (ll)1e9;
		for (j = i; j <= n; j++) {
			mn = min(mn, x[j]);
			ans = max(ans, (ll)(j - i + 1)*mn);
		}
	}
	cout << ans;
}

 

D - Logical Expression

DP로 해결했습니다.

int n;
string x[1000];
ll dp[1000][2]; // 0=false, 1=true 
int main() {
	int i; cin >> n;
	for (i = 0; i < n; i++) cin >> x[i];
	dp[n][1] = 1; dp[n][0] = 0;
	for (i = n - 1; i >= 0; i--) {
		if (x[i].compare("AND") == 0) {
			dp[i][0] = dp[i + 1][0] * 2;
			dp[i][1] = dp[i + 1][0] + dp[i + 1][1];
		}
		else {
			dp[i][0] = dp[i + 1][0] + dp[i + 1][1];
			dp[i][1] = dp[i + 1][1] * 2;
		}
	}
	cout << dp[0][0] + dp[0][1] << '\n';
}

 

E - Rotate and Flip

연산 1,2,3,4을 원점(0,0), x축(1,0), y축(0,1)에 적용하여 시간복잡도를 O(N+M+Q)로 개선했습니다.

typedef pair<ll, ll> p; 
p O[200004], X[200004], Y[200004], xy[200004];
int N, M;
void rotate_one(p &src, p &dst, int op) {
	if (op == 1) {
		dst.x = src.y;
		dst.y = -src.x;
	}
	else {
		dst.x = -src.y;
		dst.y = src.x;
	}
}
 
void rotate_xy(int idx, int op) {
	rotate_one(O[idx - 1], O[idx], op);
	rotate_one(X[idx - 1], X[idx], op);
	rotate_one(Y[idx - 1], Y[idx], op);
}
 
void flip_one(p &src, p &dst, int op, ll center) {
	// vertical flip
	if (op == 3) {
		ll dx = center - src.x;
		dst.x = src.x + dx * 2;
		dst.y = src.y;
	}
	// horizontal flip
	else {
		ll dy = center - src.y;
		dst.x = src.x;
		dst.y = src.y + dy * 2;
	}
}
 
void flip_xy(int idx, int op, ll center) {
	flip_one(O[idx - 1], O[idx], op, center);
	flip_one(X[idx - 1], X[idx], op, center);
	flip_one(Y[idx - 1], Y[idx], op, center);
}
 
void solve(p &oo, p&xx, p &yy, p &src, p &dst) {
	dst.x = oo.x; dst.y = oo.y;
 
	ll dx, dy;
 
	// x축
	dx = xx.x - oo.x; dy = xx.y - oo.y;
	dst.x += dx * src.x;
	dst.y += dy * src.x;
 
	// y축
	dx = yy.x - oo.x; dy = yy.y - oo.y;
	dst.x += dx * src.y;
	dst.y += dy * src.y;
}
 
int main() {
	O[0] = { 0,0 }, X[0] = { 1,0 }, Y[0] = { 0,1 };
	int i;
 
	cin >> N;
	for (i = 1; i <= N; i++) cin >> xy[i].x >> xy[i].y;
 
	cin >> M;
	for (i = 1; i <= M; i++) {
		int op; cin >> op;
		if (op <= 2) rotate_xy(i, op);
		else {
			int a; cin >> a;
			flip_xy(i, op, a);
		}
	}
 
	int q; cin >> q;
	while (q--) {
		int a, b; cin >> a >> b;
		p dst;
		solve(O[a], X[a], Y[a], xy[b], dst);
		cout << dst.x << ' ' << dst.y << '\n';
	}
}

 

안녕하세요. 

Round 1C 2019 후기 간단히 올려 봅니다. 추가 설명이 필요하시면 답변 부탁드립니다~

Round 2로 진출한 상황이어서 시험 종료 후에 풀어봤습니다.

문제는 세 문제였습니다. 

 

ㅁ 1번 (Robot Programming Strategy)

Greedy로 Test set 1 / 2 모두 해결 가능합니다.

나는 나를를 제외한 모든 Robot을 이겨야 합니다. (이유: 첫번째 tournament에서 나는 모든 Robot과 겨룰수 있으므로)

현재 위치를 p라고 합시다. (0부터 시작, 예를들어 Robot 1이 "RSP"이고 p=1이면 'S'를 나타냄. p=5이면 5%3(3=len("RSP") = 2이기 때문에 'P'를 나타냄)

p위치의 나를 제외한 모든 robot의 값은 다음과 같은 경우로 나뉩니다.

1) 하나의 값만 존재. 예를들어 모든 robot의 값이 'P'인 경우 나는 'S'를 내면 이깁니다. 즉, 내가 이기는 경우는 항상 있기때문에 내가 이기게 되고 현재까지의 값을 출력해 주면 됩니다.

2) 두개의 값이 존재. 예를들어 모든 robot의 값이 'R','P'인 경우 나는 'P'를 내면 되고 'R'인 robot은 지기 때문에 제외 하고 계속 진행하면 됩니다. 즉, 두개의 값중에서 이기는 경우를 나는 내면 되고, 지는 Robot을 제외한 채로 계속 진행하면 됩니다.

3) 세개의 값이 존재. 예를들어 모든 robot의 값이 'R','P','S'인 경우 나는 어떤값을 내더라도 나를 이기는 robot은 존재하기 때문에 나는 지게 됩니다. 즉, IMPOSSIBLE을 출력하면 됩니다.

Robot중에서 string길이가 가장 긴 길이만큼 실행해도 이기는 경우가 없을 경우 IMPOSSIBLE을 출력하면 됩니다.

 

ㅁ 2번 (Power Arrangers)

Interactive Problem입니다.

총 5개의 문자(A,B,C,D,E) 이고 5개의 문자를 나열하는 모든 경우중에 빠진 1가지만 찾는 문제입니다.

5개의 문자를 나열하는 경우의 수는 5!=120이고 1가지가 빠졌기 때문에 119가지가 존재합니다.

119*5개문자 = 595이기 때문에 문제에서 허용하는 F (475 / 150) 이내에 답을 찾을수 없다.

문제의 핵심은 첫번째 빠진 문자를 찾은후 두번째 문자를 찾을때에는 첫번째 문자중에 빠지지 않은 경우는 제외하면 search횟수를 많이 줄일수 있다. 예를들어서, 정답이 ABCDE인 경우 첫번째 119개 string중에 첫번째 문자의 갯수는 A=23, B~E=24가 된다. 두번째 문자 B를 찾을 경우 첫번째 문자가 B~E인 string은 제외 시키면 search횟수를 많이 줄일수 있다. 이런 방식을 할 경우 search 횟수는 첫번째 문자는 119번(5!-1), 두번째 문자는 23번(4!-1), 세번째 문자는 5번(3!-1), 네번째 문자는 1번(2!-1), 다섯번째 문자는 0번(네개의 문자를 알면 마지막 문자는 알수있음). 총 148번에 해결가능하기 때문에 Test Set 1 / 2 모두 해결가능하다

 

ㅁ 3번 Test Set 1 (Bacterial Tactics)

winning opening moves의 갯수를 구하는 문제입니다. 즉, Becca 가 이기는 첫번째 moves의 갯수를 구하면 됩니다.

전체 탐색으로 풀었습니다. 모든 opening move(2*(R+C)) 에 대해서 Becca가 이기는지 확인해 봅니다.

Becca가 이기는지 확인하는 방법도 전체 탐색으로 진행합니다. 즉, 현재 empty인 공간에 Bacteria를 설치합니다.(Horizontal / Vertical) 현재 선택 가능한 방법의 수는 (R+C) * 2 (2: Horizontal + Vertical)가 됩니다.

따라서, 시간복잡도는 O(2*(R+C) * (2*(R+C))!) = O(16*16!) 입니다. big O 시간 복잡도는 크지만 실제 시간복잡도는 크지 않은것 같습니다. (한번 play할때 선택되는 cell이 많고, radioactive material가 있어서 경우의 수가 줄어드는것 같습니다)

 

부족한글 읽어 주셔서 감사드립니다.

Code jam 참여자 여러분, 모두 화이팅 하세요!

 

안녕하세요.

Round 1B 2019 후기 간단히 올려 봅니다. 추가 설명이 필요하시면 답변 부탁드립니다~

Round 2로 진출한 상황이어서 시험 종료 후에 풀어봤습니다.

문제는 세 문제였습니다.

ㅁ 1번 Test Set1 (Manhattan Crepe Cart)
 각각의 사람에 대해서 지나가는 위치를 +1증가 시키면 됩니다. 시간 복잡도는 O(P*Q*Q)이고, P=500, Q=10이라서 
 제한 시간안에 실행이 가능합니다.

ㅁ 1번 Test Set2
 Q=100000이기 때문에 위의 방법으로는 TLE(Time Limit Expire)가 발생하기 때문에 더 좋은 방법이 필요합니다.
 X좌표와 Y좌표를 따로 생각해도 된다는 게 문제의 핵심입니다.
 현재 좌표가 (x1,y1)일 경우, 북쪽 방향은 Y [y1+1].. Y [Q]까지 1을 더하고 남쪽 방향은 Y [y1-1].. Y [0]까지 1을 더하고
 동쪽 방향은 X[x1+1]..X[Q]까지 1을 더하고 서쪽 방향은 X [x1-1].. X [0]까지 1을 더하면 됩니다.(DP로 O(1)으로 연산 가능)
 
 X배열에서 최소인 x좌표(같을 경우 작은값)와 Y배열에서 최소인 y좌표(같을 경우 작은 값)가 답이 됩니다.
 시간 복잡도는 O(Q) 입니다.
  
2번은 제가 어려워 하는 Interactive problem이었습니다.
ㅁ 2번 Test Set1 (Draupnir)
 D0, D1, D2, D3, D4, D5를 질문하면 풀 수 있습니다.
 D1-D0=R1, D2-D1=R2, D3-D2=R3, D4-D3=R4, D5-D4=R5, D0=R6를 구할 수 있습니다.ㅁ

 2번 Test Set2
 Ri 최댓값이 100(7bit)인 점과(property-1), 2^63으로 modular 한다는 점을(property-2) 이용해야 합니다.
 property-1: y = Ri * 2^i + Rj * 2^j 에서 abs(i-j)>=7인 경우 y, i, j값을 이용해서 Ri, Rj를 구할 수 있습니다.
 property-2: y = Ri * 2^i + Rj * 2^j 에서 i>=63일 경우 (Ri * 2^i) % 2^63은 0이 됩니다.
 y = R1*2^x1 + R2*2^x2 + R3*2^x3 + R4*2^x4 + R5*2^x5 + R6*2^x6 일 경우,
 property-2를 통해서 R1*2^x1=R2*2^x2=R3*2^x3=0이고(x1,x2,x3>=63), x4/x5/x6의 차이가 7 이상인 Day를 질문하면
 R4/R5/R6를 구할 수 있습니다. 이런다음에 x1, x2, x3의 차이가 7 이상인 또 다른 Day를 질문하면 R1/R2/R3를 구할 수 있습니다.(R4/R5/R6는 이미 알고 있음)

ㅁ 3번 Test Set1 (Fair Fight)
 모든 L,R(1 ≤ L ≤ R ≤ N)에 대해서 C, D에서 최댓값을 찾은 후에 각각의 최댓값의 차이가 K이하인 경우를 count 하면 된다
 시간 복잡도는 O(N^3)이고 N=100이기 때문에 시간 내에 수행 가능 하다

Round 1C 후기를 기대해 주세요.

Code jam 참여자 여러분, 모두 화이팅 하세요!

안녕하세요.

 

Round 1A 2019 후기 간단히 올려 봅니다.

 

추가 설명이 필요하시면 답변 부탁드립니다~

 

문제는 세 문제였고, 2번은 Interactive problem이었습니다.

1번은 Test Set 1은 완전 탐색으로 풀었는데, Test Set 2는 constructive method를 못 찾아서 틀렸습니다.

 

2번은 저한테는 익숙하지 않은 Interactive problem이라서 3번부터 풀었는데.. 2번은 1시 30분 동안 WA(Wrong Anser) / TLE(Time Limit Expire)만 받다가 못 풀었습니다^^;;

 

3번은 string처리 / TRIE로 풀었습니다. (Test Set 1 / 2)

 

Round 2로 진출했고, Round 2 기출 문제를 열심히 풀어볼 계획입니다.

Code jam 참여자 여러분, 모두 화이팅 하세요!

+ Recent posts