[editorial] [밀린 에디토리얼 시리즈 1] SRM 382 DIv 1

  • 일루
    일루

    드디어 에디토리얼을 쓰는 그 날이 왔습니다! 그러나 너무 옛날이라 가물가물하다능? 하악하악...
    밀린 에디토리얼이 몇개인지는 모르겠지만 쓰면서 세어보죠.
    srm382.png
    Easy(250pt, CollectingRiders)
    구현 위주의 문제입니다. 문제에서 요구하는 조건대로, 각 자리마다 판 위의 모든 나이트를 그 자리로 모으는 데 필요한 횟수를 계산해서 그 횟수의 최소치를 구하면 되겠지요. (a,b)->(c,d)까지 나이트가 움직이는 데 필요한 최소 횟수를 구하는 방법은 여러 가지가 있겠지만, 판이 작으므로 간단하게 플루이드로 처리했습니다.
    [spoiler="코드 보기!"]~~~ cpp

    #include
    #include
    #include
    #include
    #include
    #include
    using namespace std;
    int y[8] = {1, 1, 2, 2, -1, -1, -2, -2};
    int x[8] = {2, -2, 1, -1, 2, -2, 1, -1};
    class CollectingRiders
    {
    public:
    int minimalMoves(vector board)
    {
    int n = board.size();
    int m = board[0].size();
    int distance[100][100] = {0};
    for (int i=0; i for (int j=0; j for (int k=0; k for (int l=0; l distance[i*m+j][k*m+l] = 999999;
    for (int i=0; i for (int j=0; j distance[i*m+j][i*m+j] = 0;
    for (int i=0; i for (int j=0; j for (int k=0; k<8; k++)
    {
    int ny = i+y[k];
    int nx = j+x[k];
    if (ny>=0 && nx>=0 && ny distance[i*m+j][ny*m+nx] = 1;
    }
    for (int k=0; k for (int i=0; i for (int j=0; j if (distance[i][k]+distance[k][j] distance[i][j] = distance[i][k]+distance[k][j];
    int mmin = 9999999;
    for (int i=0; i {
    int now = 0;
    for (int j=0; j for (int k=0; k if (board[j][k]!='.')
    {
    int rider = board[j][k]-'0';
    int diff = distance[i][j*m+k];
    if (diff>900000) now = 9999999;
    now += (diff+rider-1)/rider;
    }
    printf("%d\n", now);
    if (now }
    if (mmin >= 1000000) return -1;
    return mmin;
    };
    };

    # 지정된 언어 [/spoiler]를 찾을 수 없습니다.
    Medium(500pt, PointsOnACircle) 원 위에 주어진 점들을 빨간색 또는 파란색으로 칠하는데 빨간색 점들은 일정 각도로 회전하면 파란색 점과 겹쳐야 한다. 칠할 수 있는 점의 최대 갯수는? 이 문제도 역시 구현 위주의 문제입니다. 대략 4중 for로 처리를 했는데... 1. 회전해야 하는 각도를 정한다. (angle=1~180도, 181도 이상은 색깔을 바꿔주면 커버된다) 2. 어느 점에서 체크를 시작할지 정한다. (starter=0~angle-1도, 그러나 예를 들어 angle이 135도라면 0->135->270->45->180->325->90->225->0 이렇게 되므로 0도에서 시작하나 45도에서 시작하나 똑같다. 일반적으로는 0~gcd(angle, 360)-1도까지만 돌리면 된다는 것을 알 수 있다.) 3. 점이 359, 0, 1, 2도에 있다고 하자. 0도에서 1도단위로 체크하면 0도, 1도 점을 선택한 뒤에 2도와 359도 점은 남는다. 하지만 359도에서 시작하면 모든 점을 선택할 수 있다. 이런 경우를 처리하기 위해서, 0도에서 시작하는 경우와 1도에서 시작하는 경우의 두 가지 경우를 시도해본다. (다른 경우는 시도할 필요가 없다) 4. x, x+angle도의 점을 두개씩 선택해보면서, 이 두 점이 존재하면 선택해준다. 이렇게 했는데 좀 더 간단하게 짤 수도 있는 모양입니다. [spoiler="코드 보기!"]~~~ cpp #include <math.h> #include <string.h> #include <vector> #include <string> #include <queue> #include <algorithm> using namespace std; class PointsOnACircle { public: int gcd(int a, int b) { while (b>0) { int c = a%b; a=b; b=c; } return a; }; int color(vector <string> points) { string pl; for (int i=0; i<points.size(); i++) pl += points[i]; pl += ' '; vector<int> pts; int data[360] = {0}; int now = 0; for (int i=0; i<pl.size(); i++) { if (pl[i] == ' ') { data[now] = 1; now = 0; } else now = now * 10 + pl[i]-'0'; } int mmax = 0; for (int angle=1; angle<=180; angle++) { int angle_gcd = gcd(angle, 360); int iteration = 360/angle_gcd; int now_sum = 0; for (int starter = 0; starter<angle_gcd; starter++) { int now_max = 0; for (int ccase = 0; ccase<2; ccase++) { int now = 0; for (int iter = 0; iter < iteration; iter++) { int angle1 = (starter+(iter+ccase)*angle)%360; int angle2 = (starter+(iter+1+ccase)*angle)%360; if (data[angle1]==1 && data[angle2]==1) { now += 2; data[angle1] = data[angle2] = 2; } } if (now>now_max) now_max = now; for (int iter = 0; iter < iteration; iter++) { int angle1 = (starter+(iter+ccase)*angle)%360; if (data[angle1]==2) data[angle1] = 1; } // printf("%d ", now); } now_sum += now_max; } if (now_sum>mmax) mmax = now_sum; // printf("%d %d %d\n", angle, angle_gcd, now_sum); } return mmax; }; }; ~~~[/spoiler] Hard(1000pt, CharmingTickets) 0-9까지 수들 중에서 주어진 몇 개 수들로만 만들어진 2*K자리 티켓이 있는데 홀수자리 짝수자리 합이 같거나 앞 절반 뒤 절반 합이 같아야 한다. 쉽지 않게 보이지만 사실은 중학교 수학으로 해결할 수 있습니다. 1) 홀수자리 짝수자리 합이 같은 경우의 수 = 앞 절반 뒤 절반의 합이 같은 경우의 수 = 임의의 K자리 수의 합이 다른 임의의 K자리 수의 합과 같은 경우의 수 K자리 수의 합 = K자리 수의 합 이 되는 경우의 수를 Count2(K, good)이라고 정의합시다. 여기서 good은 티켓을 구성하라고 주어진 수들입니다. 2) 홀수자리 짝수자리 합이 같고 앞 절반 뒤 절반의 합이 같은 경우의 수 (앞 절반에서 홀수자리 = 뒤 절반에서 짝수자리) AND (앞 절반에서 짝수자리 = 뒤 절반에서 홀수자리) 가 되어야 합니다. 이 때 K가 짝수인 경우와 홀수인 경우가 다릅니다. K가 짝수: 경우의 수는 Count2(K/2, good) * Count2(K/2, good) K가 홀수: 경우의 수는 Count2(K/2+1, good) * Count2(K/2, good) 3) 결과는 1) + 1) - 2)가 됩니다. 이제 Count2(K, good)을 구해야 합니다. 저는 DP로 해결했는데, diff[i][j] = (첫번째 i자리 수)-(두번째 i자리 수)가 j인 경우라고 놓으면 diff[i+1][j]는 diff[i+1][j-9]~diff[i+1][j+9]를 이용해서 구할 수 있습니다. 이 때 j가 0 미만이 될 수 있으므로 첨자에 적당한 수를 더해서 계산하면 됩니다. 또한 modular 999983을 계산할 때마다 적용해야 합니다. [spoiler="코드 보기!"]~~~ cpp #include <math.h> #include <string.h> #include <vector> #include <string> #include <queue> #include <algorithm> using namespace std; int modular = 999983; class CharmingTickets { public: int count2(int K, string good) { int charming[10] = {0}; for (int i=0; i<good.size(); i++) charming[good[i]-'0'] = 1; int diff_case[19] = {0}; for (int i=0; i<10; i++) for (int j=0; j<10; j++) if (charming[i] && charming[j]) diff_case[i-j+9] ++; int diff1[19999] = {0}; int diff2[19999] = {0}; diff1[0] = 1; for (int i=0; i<K; i++) { for (int j=(i+1)*18; j>=0; j--) { diff2[j] = 0; for (int k=0; k<19 && k<=j; k++) diff2[j] += diff1[j-k]*diff_case[k]; } for (int j=0; j<19999; j++) diff1[j] = diff2[j]%modular; } return diff1[9*K]; } int count(int K, string good) { int t1 = count2(K, good); int t2 = 0; if (K%2==0) { // even long long p1 = count2(K/2, good); t2 = (p1*p1)%modular; } else { // odd long long p1 = count2(K/2, good); long long p2 = count2(K/2+1, good); t2 = (p1*p2)%modular; } return (t1+t1-t2+modular)%modular; }; }; ~~~[/spoiler] <div>[이 글은 과거 홈페이지에서 이전된 글입니다. <a href="http://algospot.andromeda-express.com/zbxe/editorial/45293">원문보기</a>]</div>

    16년 전
0개의 댓글이 있습니다.
  • 정회원 권한이 있어야 커멘트를 다실 수 있습니다. 정회원이 되시려면 온라인 저지에서 5문제 이상을 푸시고, 가입 후 7일 이상이 지나셔야 합니다. 현재 문제를 푸셨습니다.