けんちょんの競プロ精進記録

競プロの精進記録や小ネタを書いていきます

AOJ 1254 Color the Map (ICPC アジア 2004 G) (450 点)

本質的には彩色数を求めるだけだけど、幾何パートがちょっと面倒...

問題概要

二次元平面上に  N 個の多角形領域 (すべて単純) がある。各領域はそれぞれある国に属している。異なる多角形領域が同一の国に属していることもある。今、国ごとに「色」を付与したい。ただし、隣接している国には異なる色を割り当てなければならない。

ある 2 つの国が隣接しているとは、それぞれの国を表す多角形の共通部分が「長さが 0 より大きい線分」になることをいう。なお、どの 2 つの国を表す多角形も 0 より大きい領域を共有しないことが保証されている。

今、各国に「色」を割り当てる。隣接している国同士には異なる色を割り当てなければならない。必要な色の個数の最小値を求めよ。

(テストケースがいくつか与えられる)

制約

  •  1 \le 国の個数 \le 10
  •  1 \le 多角形の個数 N \le 100
  •  -1000 \le 各座標値 \le 1000

考えたこと

幾何パートが面倒だけど、要するに

 M (\le 10) 頂点からなるグラフが与えられるので、そのグラフの彩色数を求めよ

という問題である。計算量はよいアルゴリズムを使えば  O(M2^{M}) でできるが、他にいろんなアルゴリズムが使えそうである。彩色数のアルゴリズムは以下を参照。

drken1215.hatenablog.com

コード

#include <bits/stdc++.h>
using namespace std;

// 彩色数
long long modpow(long long a, long long n, long long MOD) {
    long long res = 1;
    while (n > 0) {
        if (n & 1) res = res * a % MOD;
        a = a * a % MOD;
        n >>= 1;
    }
    return res;
}
int chromatic_number(const vector<vector<int> > &G) {
    const int MOD = 1000000007;
    int n = (int)G.size();
    vector<int> neighbor(n, 0);
    for (int i = 0; i < n; ++i) {
        int S = (1<<i);
        for (int j = 0; j < n; ++j)
            if (G[i][j])
                S |= (1<<j);
        neighbor[i] = S;
    }
    
    // I[S] := #. of inndepndet subset of S
    vector<int> I(1<<n);
    I[0] = 1;
    for (int S = 1; S < (1<<n); ++S) {
        int v = __builtin_ctz(S);
        I[S] = I[S & ~(1<<v)] + I[S & ~neighbor[v]];
    }
    int low = 0, high = n;
    while (high - low > 1) {
        int mid = (low + high) >> 1;
        
        // g[S] := #. of "k independent sets" which cover S just
        // f[S] := #. of "k independent sets" which consist of subseta of S
        // then
        //   f[S] = sum_{T in S} g(T)
        //   g[S] = sum_{T in S} (-1)^(|S|-|T|)f[T]
        long long g = 0;
        for (int S = 0; S < (1<<n); ++S) {
            if ((n - __builtin_popcount(S)) & 1) g -= modpow(I[S], mid, MOD);
            else g += modpow(I[S], mid, MOD);
            g = (g % MOD + MOD) % MOD;
        }
        if (g != 0) high = mid;
        else low = mid;
    }
    return high;
}


// Geometry
using DD = double;
const DD INF = 1LL<<60;      // to be set appropriately
const DD EPS = 1e-10;        // to be set appropriately
const DD PI = acos(-1.0);
DD torad(int deg) {return (DD)(deg) * PI / 180;}
DD todeg(DD ang) {return ang * 180 / PI;}

/* Point */
struct Point {
    DD x, y;
    Point(DD x = 0.0, DD y = 0.0) : x(x), y(y) {}
    friend ostream& operator << (ostream &s, const Point &p) {return s << '(' << p.x << ", " << p.y << ')';}
};
inline Point operator + (const Point &p, const Point &q) {return Point(p.x + q.x, p.y + q.y);}
inline Point operator - (const Point &p, const Point &q) {return Point(p.x - q.x, p.y - q.y);}
inline Point operator * (const Point &p, DD a) {return Point(p.x * a, p.y * a);}
inline Point operator * (DD a, const Point &p) {return Point(a * p.x, a * p.y);}
inline Point operator * (const Point &p, const Point &q) {return Point(p.x * q.x - p.y * q.y, p.x * q.y + p.y * q.x);}
inline Point operator / (const Point &p, DD a) {return Point(p.x / a, p.y / a);}
inline Point conj(const Point &p) {return Point(p.x, -p.y);}
inline Point rot(const Point &p, DD ang) {return Point(cos(ang) * p.x - sin(ang) * p.y, sin(ang) * p.x + cos(ang) * p.y);}
inline Point rot90(const Point &p) {return Point(-p.y, p.x);}
inline DD cross(const Point &p, const Point &q) {return p.x * q.y - p.y * q.x;}
inline DD dot(const Point &p, const Point &q) {return p.x * q.x + p.y * q.y;}
inline DD norm(const Point &p) {return dot(p, p);}
inline DD abs(const Point &p) {return sqrt(dot(p, p));}
inline DD amp(const Point &p) {DD res = atan2(p.y, p.x); if (res < 0) res += PI*2; return res;}
inline bool eq(const Point &p, const Point &q) {return abs(p - q) < EPS;}
inline bool operator < (const Point &p, const Point &q) {return (abs(p.x - q.x) > EPS ? p.x < q.x : p.y < q.y);}
inline bool operator > (const Point &p, const Point &q) {return (abs(p.x - q.x) > EPS ? p.x > q.x : p.y > q.y);}
inline Point operator / (const Point &p, const Point &q) {return p * conj(q) / norm(q);}

/* Line */
struct Line : vector<Point> {
    Line(Point a = Point(0.0, 0.0), Point b = Point(0.0, 0.0)) {
        this->push_back(a);
        this->push_back(b);
    }
    friend ostream& operator << (ostream &s, const Line &l) {return s << '{' << l[0] << ", " << l[1] << '}';}
};

int ccw(const Point &a, const Point &b, const Point &c) {
    if (cross(b-a, c-a) > EPS) return 1;
    if (cross(b-a, c-a) < -EPS) return -1;
    if (dot(b-a, c-a) < -EPS) return 2;
    if (norm(b-a) < norm(c-a) - EPS) return -2;
    return 0;
}
bool isinterPS(const Point &p, const Line &s) {
    return (ccw(s[0], s[1], p) == 0);
}
bool isinterSS(const Line &s, const Line &t) {
    if (eq(s[0], s[1])) return isinterPS(s[0], t);
    if (eq(t[0], t[1])) return isinterPS(t[0], s);
    return (ccw(s[0], s[1], t[0]) * ccw(s[0], s[1], t[1]) <= 0 &&
            ccw(t[0], t[1], s[0]) * ccw(t[0], t[1], s[1]) <= 0);
}
bool issame(Line l, Line s) {
    for (int it = 0; it < 2; ++it) {
        if (it == 1) swap(l, s);
        if ( !eq(l[0], s[0]) && !eq(l[0], s[1]) && ccw(s[0], s[1], l[0]) == 0 ) return true;
        if ( !eq(l[1], s[0]) && !eq(l[1], s[1]) && ccw(s[0], s[1], l[1]) == 0 ) return true;
        if ( eq(l[0], s[0]) && eq(l[1], s[1]) ) return true;
        if ( eq(l[0], s[1]) && eq(l[1], s[0]) ) return true;
    }
    return false;
}

int main() {
    int N;
    while (cin >> N, N) {
        map<string, vector<vector<Point> > > ma;
        for (int i = 0; i < N; ++i) {
            string str; 
            cin >> str;
            vector<Point> vp;
            int a, b;
            while (cin >> a) {
                if (a == -1) break;
                cin >> b;
                Point p(a, b);
                vp.push_back(p);
            }
            ma[str].push_back(vp);
        }
        int V = ma.size();
        vector<vector<int> > G(V, vector<int>(V, 0));

        int i = 0, j = 0;
        for (auto it1 : ma) {
            j = 0;
            for (auto it2 : ma) {
                if (i == j) { ++j; continue; }
                bool adj = false;
                
                for (int x = 0; x < (it1.second).size(); ++x) {
                    for (int y = 0; y < (it2.second).size(); ++y) {
                        vector<Point> vp1 = (it1.second)[x], vp2 = (it2.second)[y];
                        for (int p = 0; p < vp1.size(); ++p) {
                            for (int q = 0; q < vp2.size(); ++q) {
                                Line l(vp1[p], vp1[ (p+1)%vp1.size() ]);
                                Line m(vp2[q], vp2[ (q+1)%vp2.size() ]);
                                if (issame(l, m)) adj = true;
                            }
                        }
                    }
                }
                if (adj) G[i][j] = true;
                ++j;
            }
            ++i;
        }
        int res = chromatic_number(G);
        cout << res << endl;
    }
}