いろいろがんばりたいブログ

情報科学科の人がいろいろ書きます。

Topcoder SRM 593 div1

本番中は250のみを通した。

250

四色定理より、明らかに4以下であることはわかる。 しかし、すこし考えると、3色で塗り分け可能であることがわかる。

f:id:tomoki_imai:20131006142826p:plain

なので、0,1,2で塗れるかどうかを考えるだけで良い。 2色で塗れるかは再帰的にDFSをすればよい。(蟻本参照)

#include <vector>
#include <string>

using namespace std;

#define rep(i,n) for(int i=0;i<(int)n;i++)


const int dy[] = {-1,-1, 0, 0, 1, 1};
const int dx[] = { 0, 1, -1,1,-1, 0};

const char emp = '-';
const char col = 'X';

class HexagonalBoard{
public:
    bool solve2(int y,int x,int c,const vector<string> &board,vector<vector<char> > &color){
        int N = board.size();
        color[y][x] = c;
        rep(i,6){
            int ny=y+dy[i],nx=x+dx[i];
            if(ny >= N or ny < 0 or nx >= N or nx < 0 or board[ny][nx] == emp) continue;
            if(color[ny][nx] == c) return false;
            if(color[ny][nx] == 0 and not solve2(ny,nx,-c,board,color)) return false;
        }
        return true;
    }
    int minColors(vector <string> board){
        int N = board.size();
        {
            bool allemp = true;
            rep(i,N){
                rep(j,N){
                    allemp = allemp and board[i][j]==emp;
                }
            }
            if(allemp) return 0;
        }
        // one?
        {
            bool norel = true;
            rep(y,N){
                rep(x,N){
                    bool one = true;
                    if(board[y][x] == emp) continue;
                    for(int i=0;i<6;i++){
                        int ny=y+dy[i],nx=x+dx[i];
                        if(ny >= N or ny < 0 or nx >= N or nx < 0) continue;
                        if(board[ny][nx] != emp){
                            one = false;
                        }
                    }
                    norel = norel and one;
                }
            }
            if(norel) return 1;
        }
        {
            bool two = true;
            vector<vector<char> > color(N,vector<char>(N));
            for(int y=0;y<N;y++){
                for(int x=0;x<N;x++){
                    if(board[y][x] == emp) continue;
                    if(color[y][x] == 0){
                        if(!solve2(y,x,1,board,color)){
                            two = false;
                        }
                    }
                }
            }
            if(two) return 2;
        }
        return 3;
    }
};

450

二つの集合S,Tに分けることを考える。 そのとき、S,Tの最大の差は、(Sが最速,Tが最遅)もしくは(Sが最遅,Tが最速)の場合である。

ここで、S={0,1,2},T={3,4,5}と仮にしたとき、以下で(Sが最速,Tが最遅)が計算できる。

D = abs((A0+A1+A2)-(B3+B4+B5)) 
  = abs((A0+A1+A2+A3+A4+A5-A3-A4-A5)-(B3+B4+B5)) 
  = abs((A0+A1+A2+A3+A4+A5) - ((A3+B3)+(A4+B4)+(A5+B5))

(Sが最遅,Tが最速)のときは同様に

E = abs((B0+B1+B2+B3+B4+B5) - ((A3+B3)+(A4+B4)+(A5+B5))

よってありえる(A3+B3)+(A4+B4)+(A5+B5)の部分を列挙して、max(D,E)の最小値を計算すればよい。 これはナップサック問題のDPのようにとける。

#include <vector>
#include <algorithm>

using namespace std;

#define all(c) c.begin(),c.end()
#define rep(i,n) for(int i=0;i<(int)n;i++)

class MayTheBestPetWin{
public:
    int calc(vector <int> A, vector <int> B){
        int N = A.size();
        vector<int> S(N);
        rep(i,N) S[i] = A[i] + B[i];
        int SA = accumulate(all(A),0);
        int SB = accumulate(all(B),0);

        const int CS = accumulate(all(S),1);
        vector<char> C(CS,false);
        C[0] = true;
        rep(i,N){
            vector<char> tmp(CS,false);
            rep(j,CS){
                if(not C[j]) continue;
                tmp[j] = tmp[S[i]+j] = true;
            }
            C = tmp;
        }
        int mini = 1 << 30;
        rep(i,CS){
            if(not C[i]) continue;
            mini = min(mini,max(abs(SA-i),abs(SB-i)));
        }
        return mini;
    }
};