Experiences

[대회] Google Kick Start 2021 Round A

minigb 2021. 3. 21. 17:39

 

 

Kick Start - Google’s Coding Competitions

Hone your coding skills with algorithmic puzzles meant for students and those new to coding competitions. Participate in one round or join them all.

codingcompetitions.withgoogle.com

 

결과가 많이 아쉽다.

 

A.

코포 A와 유사

비교 대상인 쌍들 중 문자가 같은 것의 개수를 cnt라고 하면 abs(k - cnt)가 답이다.

int main()
{
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int tc; cin >> tc;

	for (int forTC = 1; forTC <= tc; forTC++) {
		int n, k; cin >> n >> k;
		string s; cin >> s;
		int cnt = 0;
		for (int i = 0; i < n / 2; i++) {
			if (s[i] != s[n - 1 - i]) cnt++;
		}

		cout << "Case #" << forTC << ": " << abs(k - cnt) << ENDL;
	}

	return 0;
}

 

B.

문제를 보자마자 엄청난 코드를 구현했다.

평소에도 문제 풀 때 일단 구현하고 보는 습관이 있다.. 바꿔야 한다.

역시나 두 번째 케이스에서 TLE가 떴고

그리고 나서 이게 prefix sum 사용하는 문제라는 게 떠올라서 고쳤다.

또 TLE가 떴다.

시간복잡도를 보니 O(N^3)이었다. 최대 n은 1000이었다.

그래서 일단 보류하고 C로 넘어갔다.

 

끝나고 풀이를 보고 다시 짰다.

int cal(int a, int b) {
	if (a < 4 && b < 4) return 0;
	
	int cnt = 0;
	if (a >= 4) {
		cnt += min((a - 2) / 2, b - 1);
	}
	if (b >= 4) {
		cnt += min((b - 2) / 2, a - 1);
	}

	return cnt;
}

int main()
{
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int tc; cin >> tc;

	for (int tcn = 1; tcn <= tc; tcn++) {
		int r, c; cin >> r >> c;
		vector<vector<int>> arr(r + 5, vector<int>(c + 5));
		for (int i = 1; i <= r; i++) {
			for (int j = 1; j <= c; j++) {
				cin >> arr[i][j];
			}
		}

		vector<vector<int>> lr, rl, ud, du;
        //각각 left->right, right->left, up->down, down->up 방향의 prefix sum을 저장
		lr = rl = ud = du = arr;

		for (int i = 1; i <= r; i++) {
			for (int j = 1; j <= c; j++) {
				if (arr[i][j]) lr[i][j] = lr[i][j - 1] + 1;
			}
		}

		for (int i = 1; i <= r; i++) {
			for (int j = c; j >= 1; j--) {
				if (arr[i][j]) rl[i][j] = rl[i][j + 1] + 1;
			}
		}

		for (int j = 1; j <= c; j++) {
			for (int i = 1; i <= r; i++) {
				if (arr[i][j]) ud[i][j] = ud[i - 1][j] + 1;
			}
		}

		for (int j = 1; j <= c; j++) {
			for (int i = r; i >= 1; i--) {
				if (arr[i][j]) du[i][j] = du[i + 1][j] + 1;
			}
		}

		
		int ans = 0;
		for (int i = 1; i <= r; i++) {
			for (int j = 1; j <= c; j++) {
				ans += cal(lr[i][j], du[i][j]);
				ans += cal(lr[i][j], ud[i][j]);
				ans += cal(rl[i][j], du[i][j]);
				ans += cal(rl[i][j], ud[i][j]);
			}
		}
		
		cout << "Case #" << tcn << ": " << ans << ENDL;
	}

	return 0;
}

cal 함수는 두 방향의 블록 개수가 a, b로 주어질 때 가능한 조합의 개수를 세는 부분이다.

만약 두 쪽이 모두 4보다 작으면 둘 다 긴 쪽이 될 수 없으므로 가능한 경우가 없다.

그리고 a, b가 긴 쪽이 될 수 있는 경우를 각각 계산해서 더한다.

 

C.

이건 코포 B번 같은 느낌이었다.

모든 높이를 priority queue에 넣고 가장 큰 값부터 뽑아서 인접한 것과 확인했다.

인접한 것이 (확인중인 높이 - 1) 보다 작다면 답 업데이트 하고 이 값으로 바꿔준다.

거의 10분만에 풀었다.

int main()
{
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int tc; cin >> tc;
	const int diry[4] = { -1, 1,0, 0 }, dirx[4] = { 0, 0, -1, 1 };

	for (int forTC = 1; forTC <= tc; forTC++) {
		int r, c; cin >> r >> c;
		vector<vector<ll>> arr(r + 5, vector<ll>(c + 5, INF));
		priority_queue<pair<ll, pair<int, int>>> pq;
		for (int i = 1; i <= r; i++) {
			for (int j = 1; j <= c; j++) {
				cin >> arr[i][j];
				pq.push({ arr[i][j], {i, j} });
			}
		}

		ll ans = 0;
		while (!pq.empty()) {
			ll h = pq.top().first;
			int y = pq.top().second.first;
			int x = pq.top().second.second;
			pq.pop();

			if (arr[y][x] != h) continue;

			for (int k = 0; k < 4; k++) {
				int yy = y + diry[k];
				int xx = x + dirx[k];
				if (arr[yy][xx] == INF) continue;

				if (arr[yy][xx] < arr[y][x] - 1) {
					ans += arr[y][x] - 1 - arr[yy][xx];
					arr[yy][xx] = arr[y][x] - 1;
					pq.push({ arr[yy][xx], {yy, xx} });
				}
			}
		}
		cout << "Case #" << forTC << ": " << ans << ENDL;
	}

	return 0;
}

 

그리고 D를 읽어보다가 첫 번째 테스트케이스는 어떻게 잘 구현할 수 있지 않을까 싶어서 열심히 짰는데

구현하기엔 훨씬 복잡한 문제라는 걸 깨달았다.

그래서 B로 돌아갔는데... 뭔가 계속 놓치고 있었다

결국 종료 전에 못 풀었고.

 

여러모로 정말 많이 아쉽다.