-
알고스팟 팬미팅 FANMEETINGPS 2020. 1. 17. 17:47
https://algospot.com/judge/problem/read/FANMEETING
algospot.com :: FANMEETING
팬미팅 문제 정보 문제 가장 멤버가 많은 아이돌 그룹으로 기네스 북에 올라 있는 혼성 팝 그룹 하이퍼시니어가 데뷔 10주년 기념 팬 미팅을 개최했습니다. 팬 미팅의 한 순서로, 멤버들과 참가한 팬들이 포옹을 하는 행사를 갖기로 했습니다. 하이퍼시니어의 멤버들은 우선 무대에 일렬로 섭니다. 팬 미팅에 참가한 M명의 팬들은 줄을 서서 맨 오른쪽 멤버에서부터 시작해 한 명씩 왼쪽으로 움직이며 멤버들과 하나씩 포옹을 합니다. 모든 팬들은 동시에 한 명씩 움직입니
algospot.com
<접근방법>
입력값이 커서 완전탐색으로는 안된다는 것을 알 수 있다.(댓글보면 정답처리가 되는 거 같긴 합니다.)
문제를 보면 무언가 규칙이 있어 보입니다.(결국 발견실패..ㅠㅠ)
책을 참고해서 보면 이러한 규칙이 있다는 것을 알 수 있습니다.
그림1 밑줄 친 부분을 나열해서 보면..
A의 순서만 뒤집어 준다면 문제에서 정의한 팬미팅의 모습이 나옵니다.
남자를 1, 여자를 0이라고 하면 An*Bn의 값이 0이 되면 포옹하는 것이 되겠죠.
그림1의 밑줄 친 부분의 연산이 끝났을 때, 값이 0이 된다면 모두가 포옹하는 것이 됩니다.
그렇다면 곱셈 연산의 시간을 줄이는 것이 관건이겠네요.
일반적인 두 큰 수를 곱셈하는 알고리즘을 사용한다면 O(n^2)으로 완전탐색과 차이점이 없습니다.
그래서 카라츠바 알고리즘을 사용하여 곱셈을 구현합니다.
<코드>
카라츠바 알고리즘의 normalize는 자릿수 처리인데 이 문제에서는 두 수 모두 0, 1로만 이루어져 있기 때문에
필요가 없습니다.
#include <iostream> #include <algorithm> #include <vector> #include <string> using namespace std; int tc; void normalize(vector<int>& c) { //가장 큰 자릿수 올림땜에 c.push_back(0); for (int i = 0; i < c.size()-1; i++) { if (c[i] < 0) { int borrow = (abs(c[i]) + 9) / 10; c[i + 1] -= borrow; c[i] += borrow * 10; } else { c[i + 1] += c[i] / 10; c[i] %= 10; } } while (c.size() > 1 && c.back() == 0) c.pop_back(); } vector<int> multiply(const vector<int> &a, const vector<int> &b) { vector<int> c(a.size() + b.size() + 1, 0); for (int i = 0; i < a.size(); i++) { for (int j = 0; j < b.size(); j++) { c[i + j] += (a[i] * b[j]); } } //normalize(c); return c; } //a += b*(10^k) void addTo(vector<int>& a, const vector<int>& b, int k) { a.resize(max(a.size(), b.size() + k)); for (int i = 0; i < b.size(); i++) { a[i + k] += b[i]; } ////정규화 normalize //for (int i = 0; i < a.size(); i++) { // if (a[i] > 10) { // a[i + 1] += a[i] % 10; // a[i] /= 10; // } //} } //a -= b a>=b를 가정 void subFrom(vector<int>& a, const vector<int>& b) { a.resize(max(a.size(), b.size())+1); //+1 없어도 된다 for (int i = 0; i < b.size(); i++) { a[i] -= b[i]; } ////정규화 //for (int i = 0; i < a.size(); i++) { // if (a[i] < 0) { // a[i] += 10; // a[i + 1] -= 1; // } //} } vector<int> karatsuba(const vector<int>& a, const vector<int>& b) { //a가 사이즈가 더 작으면 바꿔서 계산 int an = a.size(), bn = b.size(); if (an < bn) return karatsuba(b, a); //기저사례 //-a나 b가 비어있는 경우 if (an == 0 || bn == 0) return vector<int>();//빈 벡터 //system("pause"); //-사이즈가 50보다 작으면 그냥 곱셈으로 if (an <= 50) return multiply(a, b); int half = an / 2; //a와 b를 반띵 vector<int> a0(a.begin(), a.begin() + half); vector<int> a1(a.begin() + half, a.end()); vector<int> b0(b.begin(), b.begin() + min<int>(bn, half));//<int>가 왜 붙냐 vector<int> b1(b.begin() + min<int>(bn, half), b.end()); //z 구하기 vector<int> z2 = karatsuba(a1, b1); vector<int> z0 = karatsuba(a0, b0); addTo(a0, a1, 0); addTo(b0, b1, 0); //z1 = (a0 * b0) - z0 - z2 vector<int> z1 = karatsuba(a0, b0); subFrom(z1, z0); subFrom(z1, z2); //z 합치기 vector<int> res; addTo(res, z0, 0); addTo(res, z1, half); addTo(res, z2, half * 2); return res; } void simul(const string& s1, const string& s2) { int n = s1.size(), m = s2.size(); vector<int> A(n), B(m); for (int i = 0; i < n; i++) { A[i] = (s1[i] == 'M') ? 1 : 0; } for (int i = 0; i < m; i++) { B[m-i-1] = (s2[i] == 'M') ? 1 : 0; } vector<int>C = karatsuba(A, B); int cnt = 0; for (int i = n - 1; i < m; i++) { if (C[i] == 0) cnt++; } cout << cnt << '\n'; } int main() { ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); cin >> tc; while (tc--) { string s[2]; for (int i = 0; i < 2; i++) { cin >> s[i]; } simul(s[0], s[1]); } return 0; }
[참고]
-프로그래밍대회에서 배우는 알고리즘 문제 해결 전략1
반응형'PS' 카테고리의 다른 글
알고스팟 타일링 TILING2 미완 (0) 2020.01.22 알고스팟 최대 증가 부분 수열 LIS (0) 2020.01.20 알고스팟 시계맞추기 Synchronizing Clocks (0) 2020.01.15 알고스팟 외발뛰기 JUMPGAME (0) 2020.01.15 종만북 dp 개론 미완 (0) 2020.01.14