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

  • User Avatar
    일루

    드디어 에디토리얼을 쓰는 그 날이 왔습니다! 그러나 너무 옛날이라 가물가물하다능? 하악하악...
    밀린 에디토리얼이 몇개인지는 모르겠지만 쓰면서 세어보죠.
    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;
    };
    };

    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>
    

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