알고리즘/algospot

algospot ZIMBABWE

꾸준함. 2018. 2. 5. 23:09

문제 링크입니다: https://algospot.com/judge/problem/read/ZIMBABWE

이 문제도 비트마스크를 사용했습니다.

하지만 이 문제는 비트마스크보다는 가격이 아닌 가격의 나머지만을 전달하더라도 M의 배수 여부를 판별할 수 있다는 것이 제일 중요했던 것 같습니다.

그리고 중복된 숫자를 세지 않는다는 것 또한 중요합니다!


#include <iostream>

#include <string>

#include <vector>

#include <algorithm>

#include <cstring> //memset

#include <bitset>

using namespace std;

 

//digits: egg의 자릿수들을 정렬한 것

string egg, digits;

int N, M; //N egg의 자릿수, egg M으로 나누어 떨어져야한다

const int MOD = 1000000007;

//자릿수가 최대 14자리이므로 1<<14, M<=20, 그리고 egg보다 작은지 여부

int cache[1 << 14][20][2];

 

/*

//egg의 자릿수로 만들 수 있는 숫자들을 모두 출력

//price:지금까지 만든 가격

//taken:각 자릿수의 사용 여부

void generate(string price, bool taken[15]) //가능한 가격을 모두 구하는 함수(완전 탐색)

{

        //기저 사례

        if (price.size() == N)

        {

               if (price < egg)

                       cout << price << endl;

               return;

        }

        for (int i = 0; i < N; i++)

        {

               //중복을 방지하기 위해 egg의 각 자릿수를 정렬해서 digits에 저장한 뒤

               //이번에 사용할 자릿수 바로 앞자리가 없거나

               //이번 자릿수랑 다르거나, 이미 사용된 경우만 사용하도록

               if (!taken[i] && (i == 0 || digits[i - 1] != digits[i] || taken[i - 1]))

               {

                       taken[i] = true;

                       generate(price + digits[i], taken);

                       taken[i] = false;

               }

        }

}

*/

 

//과거 가격을 앞 자리부터 채워나가고 있다

//index:이번에 채울 자리의 인덱스

//taken:지금까지 사용한 자릿수들의 집합

//remainder: 지금까지 만든 가격의 M에 대한 나머지

//less: 지금까지 만든 가격이 이미 egg보다 작으면 1, 아니면 0

long long price(int index, int taken, int remainder, int less)

{

        //기저 사례

        if (index == N)

               return (less && remainder==0) ? 1 : 0;

        //메모이제이션

        int &result = cache[taken][remainder][less];

        if (result != -1)

               return result;

        result = 0;

        for (int next = 0; next < N; next++)

               if ((taken & (1 << next)) == 0) //next번째 숫자를 사용한적이 없다면

               {

                       //과거 가격은 새 가격보다 항상 작아야만 한다

                       if (!less && egg[index] < digits[next])

                              continue;

                       //같은 숫자는 한번만 선택

                       if (next > 0 && digits[next - 1] == digits[next] && (taken & (1 << (next - 1))) == 0) //next-1번째 숫자를 이미 사용했고 연속해서 같은 숫자이면

                              continue;

                       int nextTaken = taken | (1 << next); //next번째 숫자를 사용했다고 표기

                       //나머지가 같을 경우 앞에 어떤 수가 왔는지는 중요하지 않다. 나머지만 전달해도 계란 가격이 m의 배수인지 알 수 있다

                       int nextRemainder = (remainder * 10 + digits[next] - '0') % M; //다음 나머지

                       int nextLess = less || egg[index] > digits[next]; //작은 숫자 여부 확인

                       result += price(index + 1, nextTaken, nextRemainder, nextLess);

                       result %= MOD;

               }

        return result;

}

 

int main(void)

{

        int test_case;

        cin >> test_case;

        if (test_case < 0 || test_case>50)

               exit(-1);

 

        for (int i = 0; i < test_case; i++)

        {

               N = 0;

               vector<long long> candidate;

               memset(cache, -1, sizeof(cache));

               cin >> egg >> M;

               long long num = atoll(egg.c_str());

               candidate.clear();

               while (num > 0)

               {

                       candidate.push_back(num % 10);

                       num /= 10;

                       N++;

               }

               sort(candidate.begin(), candidate.end());

               for (int j = 0; j < N; j++)

                       digits += candidate[j]+'0';

               cout << price(0, 0, 0, 0) << endl;

        }

        return 0;

}


개발환경:Visual Studio 2017


지적, 조언, 질문 환영입니다! 댓글 남겨주세요~


[참고] 프로그래밍 대회에서 배우는 알고리즘 문제해결전략


반응형

'알고리즘 > algospot' 카테고리의 다른 글

algospot TICTACTOE  (0) 2018.02.07
algospot RESTORE  (4) 2018.02.06
algospot TSP2  (0) 2018.02.05
algospot DRAGON  (3) 2018.02.03
algospot KLIS  (0) 2018.02.02