Problem:
Problem G:
Color the Map
You were lucky enough to get a map just before entering the legendary magical mystery world.
The map shows the whole area of your planned exploration, including several countries with
complicated borders. The map is clearly drawn, but in sepia ink only; it is hard to recognize at
a glance which region belongs to which country, and this might bring you into severe danger. You
have decided to color the map before entering the area. “A good deal depends on preparation,”
you talked to yourself.
Each country has one or more territories, each of which has a polygonal shape. Territories
belonging to one country may or may not “touch” each other, i.e. there may be disconnected
territories. All the territories belonging to the same country must be assigned the same color.
You can assign the same color to more than one country, but, to avoid confusion, two countries
“adjacent” to each other should be assigned different colors. Two countries are considered to be
“adjacent” if any of their territories share a border of non-zero length.
Write a program that finds the least number of colors required to color the map.
Input
The input consists of multiple map data. Each map data starts with a line containing the total
number of territories
n
, followed by the data for those territories.
n
is a positive integer not
more than 100. The data for a territory with
m
vertices has the following format:
String
x
1
y
1
x
2
y
2
...
x
m
y
m
-1
“
String
” (a sequence of alphanumerical characters) gives the name of the country it belongs to.
A country name has at least one character and never has more than twenty. When a country
has multiple territories, its name appears in each of them.
Remaining lines represent the vertices of the territory. A vertex data line has a pair of nonneg-
ative integers which represent the
x
- and
y
-coordinates of a vertex.
x
- and
y
-coordinates are
separated by a single space, and y-coordinate is immediately followed by a newline. Edges of
the territory are obtained by connecting vertices given in two adjacent vertex data lines, and byconnecting vertices given in the last and the first vertex data lines. None of
x
- and
y
-coordinates
exceeds 1000. Finally, -1 in a line marks the end of vertex data lines. The number of vertices
m
does not exceed 100.
You may assume that the contours of polygons are simple, i.e. they do not cross nor touch
themselves. No two polygons share a region of non-zero area. The number of countries in a map
does not exceed 10.
The last map data is followed by a line containing only a zero, marking the end of the input
data.
Output
For each map data, output one line containing the least possible number of colors required to
color the map satisfying the specified conditions.
Sample Input
6
Blizid
0 0
60 0
60 60
0 60
0 50
50 50
50 10
0 10
-1
Blizid
0 10
10 10
10 50
0 50
-1
Windom
10 10
50 10
40 20
20 20
20 40
10 50
-1
Accent
50 10
50 50
35 50
35 25
-1
Pilot
35 25
35 50
10 50
-1
Blizid
20 20
40 20
20
...(truncated)

Submitted solutions:

code_snippet: #include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const double eps=1e-5;
struct Point
{   int x,y;
    Point(){}
    Point(int _,int __){x=_;y=__;}
};
bool operator == (const Point &x,const Point &y){return x.x==y.x&&x.y==y.y;}
struct Polygon{int tt;Point p[105];}P[105];
char s[15][100005],st[100005];
int bh[1005],m[105][105],vst[105],tt;
int Cross(Point C,Point A,Point B)
{   return (A.x-C.x)*(B.y-C.y)-(B.x-C.x)*(A.y-C.y);}
bool onseg(Point p1,Point p2,Point p)//p是否在[p1,p2]上 
{   int minx=min(p1.x,p2.x),maxx=max(p1.x,p2.x),miny=min(p1.y,p2.y),maxy=max(p1.y,p2.y);
    return Cross(p,p1,p2)==0&&minx<=p.x&&p.x<=maxx&&miny<=p.y&&p.y<=maxy;
}
double Slope(Point A,Point B)
{   if(A.x==B.x)return 999;
    if(A.x>B.x)swap(A,B);
    return atan2(A.y-B.y,A.x-B.x);
}
double Abs(double x){if(x<0)x=-x;return x;}
bool Inter(int x1,int x2)
{   int i,j;
    for(i=1;i<=P[x1].tt;i++)
       for(j=1;j<=P[x2].tt;j++)
         {if(Abs(Slope(P[x1].p[i],P[x1].p[i+1])-Slope(P[x2].p[j],P[x2].p[j+1]))>eps)continue;
		  if(P[x1].p[i].x==P[x1].p[i+1].x&&P[x1].p[i].x==P[x2].p[j].x&&P[x2].p[j].x==P[x2].p[j+1].x)
		    {if(max(P[x1].p[i].y,P[x1].p[i+1].y)<=min(P[x2].p[j].y,P[x2].p[j+1].y))continue;
             if(min(P[x1].p[i].y,P[x1].p[i+1].y)>=max(P[x2].p[j].y,P[x2].p[j+1].y))continue;
			}
		  else
		    {if(max(P[x1].p[i].x,P[x1].p[i+1].x)<=min(P[x2].p[j].x,P[x2].p[j+1].x))continue;
             if(min(P[x1].p[i].x,P[x1].p[i+1].x)>=max(P[x2].p[j].x,P[x2].p[j+1].x))continue;
		    }
		  if(onseg(P[x1].p[i],P[x1].p[i+1],P[x2].p[j])
		    +onseg(P[x1].p[i],P[x1].p[i+1],P[x2].p[j+1])
		    +onseg(P[x2].p[j],P[x2].p[j+1],P[x1].p[i])
		    +onseg(P[x2].p[j],P[x2].p[j+1],P[x1].p[i+1])
		    >=2)return true;
		 }
	return false;
}
bool DFS(int nowdep,int clr)
{   if(nowdep==tt+1)return true;
    int i,j,bj;
    for(i=1;i<=clr;i++)//颜色 
      {for(j=1,bj=0;j<nowdep;j++)//验证 
	     {if(m[nowdep][j]&&i==vst[j]){bj=1;break;}}
	   if(!bj)
	     {vst[nowdep]=i;
		  if(DFS(nowdep+1,clr))return true;
		  vst[nowdep]=0;
		 }
	  }
	return false;
}
int main()
{   int N,i,j,bj,len,x,y;
	while(1)
	  {scanf("%d",&N);
	   if(!N)break;
	   tt=0;
	   for(i=1;i<=N;i++)
	     {scanf("%s",st+1);
		  for(j=1,bj=0;j<=tt;j++)
		    {if(strcmp(st+1,s[j]+1)==0){bj=1;bh[i]=j;break;}}
		  if(!bj)
		    {bh[i]=++tt;
			 len=strlen(st+1);
			 for(j=1;j<=len+1;j++)s[tt][j]=st[j];
			}
		  P[i].tt=0;
		  while(1)
		    {scanf("%d",&x);
		     if(x==-1)break;
		     scanf("%d",&y);
		     P[i].tt++;P[i].p[P[i].tt]=Point(x,y);
			}
		  P[i].p[P[i].tt+1]=P[i].p[1];
		 }
	   for(i=1;i<=tt;i++)for(j=1;j<=tt;j++)m[i][j]=0;
	   for(i=1;i<=tt;i++)vst[i]=0;
	   for(i=1;i<=N;i++)
	      for(j=i+1;j<=N;j++)
	         if(bh[i]!=bh[j]&&Inter(i,j))m[bh[i]][bh[j]]=m[bh[j]][bh[i]]=1;
	   for(i=1;;i++)if(DFS(1,i)){printf("%d\n",i);break;}
	  }
	return 0;
}
accuracy: 1
time_ms: 140
memory_kb: 3948
final_rank: 16

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b) for (int i = (int)(a); i < (int)(b); i++)
#define rrep(i, a, b) for (int i = (int)(b)-1; i >= (int)(a); i--)
#define ALL(v) (v).begin(), (v).end()
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) (int)v.size()
#define MIN(v) *min_element(ALL(v))
#define MAX(v) *max_element(ALL(v))
#define LB(v, x) int(lower_bound(ALL(v), (x)) - (v).begin())
#define UB(v, x) int(upper_bound(ALL(v), (x)) - (v).begin())

using uint = unsigned int;
using ll = long long int;
using ull = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;
const int inf = 0x3fffffff;
const ll INF = 0x1fffffffffffffff;

template <typename T> inline bool chmax(T &a, T b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T> inline bool chmin(T &a, T b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T, typename U> T ceil(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? (x + y - 1) / y : x / y);
}
template <typename T, typename U> T floor(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? x / y : (x - y + 1) / y);
}
template <typename T> int popcnt(T x) {
    return __builtin_popcountll(x);
}
template <typename T> int topbit(T x) {
    return (x == 0 ? -1 : 63 - __builtin_clzll(x));
}
template <typename T> int lowbit(T x) {
    return (x == 0 ? -1 : __builtin_ctzll(x));
}

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << "P(" << p.first << ", " << p.second << ")";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) {
    os << "{";
    for (int i = 0; i < vec.size(); i++) {
        os << vec[i] << (i + 1 == vec.size() ? "" : ", ");
    }
    os << "}";
    return os;
}
template <typename T, typename U>
ostream &operator<<(ostream &os, const map<T, U> &map_var) {
    os << "{";
    for (auto itr = map_var.begin(); itr != map_var.end(); itr++) {
        os << "(" << itr->first << ", " << itr->second << ")";
        itr++;
        if (itr != map_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const set<T> &set_var) {
    os << "{";
    for (auto itr = set_var.begin(); itr != set_var.end(); itr++) {
        os << *itr;
        ++itr;
        if (itr != set_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
#ifdef LOCAL
#define show(...) _show(0, #__VA_ARGS__, __VA_ARGS__)
#else
#define show(...) true
#endif
template <typename T> void _show(int i, T name) {
    cerr << '\n';
}
template <typename T1, typename T2, typename... T3>
void _show(int i, const T1 &a, const T2 &b, const T3 &...c) {
    for (; a[i] != ',' && a[i] != '\0'; i++)
        cerr << a[i];
    cerr << ":" << b << " ";
    _show(i + 1, a, c...);
}

/**
 * @brief template
 */

using Point = pair<int,int>;

bool isIntersect(int L1, int R1, int L2, int R2) {
    if (L1 > R1) swap(L1,R1);
    if (L2 > R2) swap(L2,R2);
    if (R2 <= L1 || R1 <= L2) return false;
    else return true;
}

bool isAdLine(Point A1, Point A2, Point B1, Point B2) {
    if (A1.first > A2.first) swap(A1,A2);
    if (B1.first > B2.first) swap(B1,B2);
    int AX = A2.first - A1.first, AY = A2.second - A1.second;
    int BX = B2.first - B1.first, BY = B2.second - B1.second;
    int ABX = B1.first - A1.first, ABY = B1.second - A1.second;
    if (AY*BX != AX*BY) return false;
    if (AY*ABX != AX*ABY) return false;
    if (A1.first == A2.first) return isIntersect(A1.second,A2.second,B1.second,B2.second);
    else return isIntersect(A1.first,A2.first,B1.first,B2.first);
}

bool isAdPoly(vector<Point> P1, vector<Point> P2) {
    int N = P1.size(), M = P2.size();
    rep(i,0,N) {
        rep(j,0,M) {
            if(isAdLine(P1[i],P1[(i+1)%N],P2[j],P2[(j+1)%M])) return true;
        }
    }
    return false;
}

int main() {
while(1) {
    int N;
    cin >> N;
    if (N == 0) return 0;
    map<string,int> mp;
    vector<int> ID(N);
    vector<vector<Point>> Poly(N);
    int Cur = 0;
    rep(i,0,N) {
        string S;
        cin >> S;
        if (mp.count(S)) ID[i]=mp[S];
        else ID[i] = Cur, mp[S] = Cur, Cur++;
        int X, Y;
        while(1) {
            cin >> X;
            if (X == -1) break;
            cin >> Y;
            Poly[i].push_back({X,Y});
        }
    }
    int M = Cur;
    vector<vector<bool>> G(M,vector<bool>(M,false));
    rep(i,0,N) {
        rep(j,i+1,N) {
            if (ID[i] == ID[j]) continue;
            if (isAdPoly(Poly[i],Poly[j])) G[ID[i]][ID[j]] = G[ID[j]][ID[i]] = true;
        }
    }
    vector<bool> OK(1<<M,false);
    rep(i,0,1<<M) {
        bool check = true;
        rep(j,0,M) {
            if (!(i&(1<<j))) continue;
            rep(k,0,M) {
                if (!(i&(1<<k))) continue;
                if (G[j][k]) check = false;
            }
        }
        if (check) OK[i] = true;
    }
    vector<int> DP(1<<M,inf);
    DP[0] = 0;
    rep(i,0,1<<M) {
        rep(j,0,1<<M) {
            if (OK[j]) chmin(DP[i|j],DP[i]+1);
        }
    }
    cout << DP[(1<<M)-1] << endl;
}
}
accuracy: 1
time_ms: 10
memory_kb: 3388
final_rank: 3

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
using P = pair<ll, ll>;
#define rep(i, a, b) for(ll i = a; i < b; ++i)
#define rrep(i, a, b) for(ll i = a; i >= b; --i)
constexpr ll inf = 4e18;
struct SetupIO {
    SetupIO() {
        ios::sync_with_stdio(0);
        cin.tie(0);
        cout << fixed << setprecision(30);
    }
} setup_io;
using Real = long double;
const Real EPS = 1e-8, PI = acos(Real(-1.0));
int sign(const Real& r) {
    if(r <= -EPS) return -1;
    if(r >= +EPS) return +1;
    return 0;
}
bool eq(const Real& a, const Real& b) {
    return sign(a - b) == 0;
}
using Point = complex<Real>;
istream& operator>>(istream& is, Point& p) {
    Real a, b;
    is >> a >> b;
    p = Point(a, b);
    return is;
}
ostream& operator<<(ostream& os, const Point& p) {
    return os << p.real() << " " << p.imag();
}
Point operator*(const Point& p, const Real& d) {
    return Point(p.real() * d, p.imag() * d);
}
Point operator/(const Point& p, const Real& d) {
    return Point(p.real() / d, p.imag() / d);
}
Point rot(const Point& p, Real theta) {
    return p * Point(cos(theta), sin(theta));
}
Real dot(const Point& p1, const Point& p2) {
    return (conj(p1) * p2).real();
}
Real cross(const Point& p1, const Point& p2) {
    return (conj(p1) * p2).imag();
}
Real dist(const Point& p1, const Point& p2) {
    return abs(p1 - p2);
}
bool comp_x(const Point& p1, const Point& p2) {
    return eq(p1.real(), p2.real()) ? p1.imag() < p2.imag() : p1.real() < p2.real();
}
bool comp_y(const Point& p1, const Point& p2) {
    return eq(p1.imag(), p2.imag()) ? p1.real() < p2.real() : p1.imag() < p2.imag();
}
bool comp_arg(const Point& p1, const Point& p2) {
    return arg(p1) < arg(p2);
}
int ccw(const Point& a, Point b, Point c) {
    b -= a;
    c -= a;
    if(sign(cross(b, c)) == 1) return 1;
    if(sign(cross(b, c)) == -1) return -1;
    if(sign(dot(b, c)) == -1) return +2;
    if(norm(b) < norm(c)) return -2;
    return 0;
}
Real closest_pair(vector<Point> ps) {
    if((int)ps.size() <= 1) return 1e18;
    sort(ps.begin(), ps.end(), comp_x);
    vector<Point> memo(ps.size());
    auto func = [&](auto& func, int l, int r) -> Real {
        if(r - l <= 1) return 1e18;
        int m = (l + r) >> 1;
        Real x = ps[m].real();
        Real res = min(func(func, l, m), func(func, m, r));
        inplace_merge(ps.begin() + l, ps.begin() + m, ps.begin() + r, comp_y);
        int cnt = 0;
        for(int i = l; i < r; ++i) {
            if(abs(ps[i].real() - x) >= res) continue;
            for(int j = 0; j < cnt; ++j) {
                Point d = ps[i] - memo[cnt - j - 1];
                if(d.imag() >= res) break;
                res = min(res, abs(d));
            }
            memo[cnt++] = ps[i];
        }
        return res;
    };
    return func(func, 0, (int)ps.size());
}
struct Line {
    Point a, b;
    Line() = default;
    Line(const Point& a, const Point& b)
        : a(a), b(b) {}
};
using Segment = Line;
bool is_parallel(const Line& a, const Line& b) {
    return eq(cross(a.b - a.a, b.b - b.a), 0.0);
}
bool is_orthogonal(const Line& a, const Line& b) {
    return eq(dot(a.b - a.a, b.b - b.a), 0.0);
}
Point projection(const Line& l, const Point& p) {
    Real t = dot(p - l.a, l.b - l.a) / norm(l.b - l.a);
    return l.a + (l.b - l.a) * t;
}
Point reflection(const Line& l, const Point& p) {
    return p + (projection(l, p) - p) * 2.0;
}
bool is_intersect_lp(const Line& l, const Point& p) {
    return abs(ccw(l.a, l.b, p)) != 1;
}
bool is_intersect_sp(const Segment& s, const Point& p) {
    return ccw(s.a, s.b, p) == 0;
}
bool is_intersect_ll(const Line& l1, const Line& l2) {
    if(!eq(cross(l1.b - l1.a, l2.b - l2.a), 0.0)) return true;
    return eq(cross(l1.b - l1.a, l2.b - l1.a), 0.0);
}
bool is_intersect_ls(const Line& l, const Segment& s) {
    return sign(cross(l.b - l.a, s.a - l.a) * cross(l.b - l.a, s.b - l.a)) <= 0;
}
bool is_intersect_sl(const Segment& s, const Line& l) {
    return is_intersect_ls(l, s);
}
bool is_intersect_ss(const Segment& s1, const Segment& s2) {
    if(ccw(s1.a, s1.b, s2.a) * ccw(s1.a, s1.b, s2.b) > 0) return false;
    return ccw(s2.a, s2.b, s1.a) * ccw(s2.a, s2.b, s1.b) <= 0;
}
vector<Point> intersection_ll(const Line& l1, const Line& l2) {
    vector<Point> res;
    if(!is_intersect_ll(l1, l2)) return res;
    Real a = cross(l1.b - l1.a, l2.b - l2.a);
    Real b = cross(l1.b - l1.a, l1.b - l2.a);
    if(eq(a, 0.0) and eq(b, 0.0)) {
        res.push_back(l2.a);
    } else {
        res.push_back(l2.a + (l2.b - l2.a) * b / a);
    }
    return res;
}
vector<Point> intersection_ss(const Segment& s1, const Segment& s2) {
    return is_intersect_ss(s1, s2) ? intersection_ll(Line(s1), Line(s2)) : vector<Point>();
}
Real dist_lp(const Line& l, const Point& p) {
    return abs(p - projection(l, p));
}
Real dist_sp(const Segment& s, const Point& p) {
    Point h = projection(s, p);
    if(is_intersect_sp(s, h)) return abs(h - p);
    return min(abs(s.a - p), abs(s.b - p));
}
Real dist_ll(const Line& l1, const Line& l2) {
    if(is_intersect_ll(l1, l2)) return 0.0;
    return dist_lp(l1, l2.a);
}
Real dist_ss(const Segment& s1, const Segment& s2) {
    if(is_intersect_ss(s1, s2)) return 0.0;
    return min({dist_sp(s1, s2.a), dist_sp(s1, s2.b), dist_sp(s2, s1.a), dist_sp(s2, s1.b)});
}
Real dist_ls(const Line& l, const Segment& s) {
    if(is_intersect_ls(l, s)) return 0.0;
    return min(dist_lp(l, s.a), dist_lp(l, s.b));
}
Real dist_sl(const Segment& s, const Line& l) {
    return dist_ls(l, s);
}
int main(void) {
    while(true) {
        int n;
        cin >> n;
        if(n == 0) break;
        vector<int> c(n);
        vector<vector<Point>> p(n);
        map<string, int> mp;
        vector<vector<int>> area;
        int cnt = 0;
        rep(i, 0, n) {
            string country;
            cin >> country;
            if(mp.find(country) == mp.end()) {
                mp[country] = cnt;
                area.push_back({});
                cnt++;
            }
            c[i] = mp[country];
            area[c[i]].push_back(i);
            while(true) {
                int x, y;
                cin >> x;
                if(x == -1) break;
                cin >> y;
                p[i].emplace_back(x, y);
            }
            p[i].push_back(p[i][0]);
        }
        int m = area.size();
        vector<vector<bool>> adj(m, vector<bool>(m));
        rep(i, 0, n) {
            rep(j, i + 1, n) {
                bool flag = false;
                rep(k, 0, (int)p[i].size() - 1) {
                    rep(l, 0, (int)p[j].size() - 1) {
                        Point dir = p[i][k + 1] - p[i][k];
                        Segment a1 = Segment(p[i][k] + EPS * dir, p[i][k + 1]);
                        Segment a2 = Segment(p[i][k], p[i][k + 1] - EPS * dir);
                        Segment b = Segment(p[j][l], p[j][l + 1]);
                        if(!is_parallel(a1, b) or !is_intersect_ss(a1, b) or !is_intersect_ss(a2, b)) continue;
                        flag = true;
                    }
                }
                if(flag) {
                    adj[c[i]][c[j]] = true;
                    adj[c[j]][c[i]] = true;
                }
            }
        }
        vector<int> color(m, -1);
        int ans = m;
        auto dfs = [&](auto& dfs, int cur, int col) -> void {
            if(cur == (int)area.size()) {
                ans = min(ans, col + 1);
                return;
            }
            rep(i, 0, col + 2) {
                color[cur] = i;
                bool flag = true;
                rep(j, 0, cur) {
                    if(adj[cur][j] and color[cur] == color[j]) {
                        flag = false;
                    }
                }
                if(flag) {
                    dfs(dfs, cur + 1, max(col, (int)i));
                }
                color[cur] = -1;
            }
        };
        color[0] = 0;
        dfs(dfs, 1, 0);
        cout << ans << '\n';
    }
}
accuracy: 1
time_ms: 30
memory_kb: 3372
final_rank: 11

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

struct Point {
	int px, py;
};

bool operator<(const Point& a1, const Point& a2) {
	if (a1.px < a2.px) return true;
	if (a1.px > a2.px) return false;
	if (a1.py < a2.py) return true;
	return false;
}

bool operator<=(const Point& a1, const Point& a2) {
	if (a1.px < a2.px) return true;
	if (a1.px > a2.px) return false;
	if (a1.py < a2.py) return true;
	if (a1.py > a2.py) return false;
	return true;
}

Point operator-(const Point& a1, const Point& a2) {
	return Point{ a1.px - a2.px, a1.py - a2.py };
}

int crs(Point p1, Point p2) {
	return p1.px * p2.py - p1.py * p2.px;
}

bool ShareBorder(Point p1, Point p2, Point p3, Point p4) {
	if (crs(p2 - p1, p3 - p1) != 0) return false;
	if (crs(p2 - p1, p4 - p1) != 0) return false;
	if (crs(p4 - p3, p1 - p3) != 0) return false;
	if (crs(p4 - p3, p2 - p3) != 0) return false;
	if (p2 < p1) swap(p2, p1);
	if (p4 < p3) swap(p4, p3);
	if (p2 <= p3 || p4 <= p1) return false;
	return true;
}

int N;
bool graph[109][109];
string Name[109];
vector<Point> P[109];

void Initialize() {
	for (int i = 0; i < 109; i++) {
		P[i].clear();
		for (int j = 0; j < 109; j++) graph[i][j] = false;
	}
}

bool Share(vector<Point> L1, vector<Point> L2) {
	for (int i = 0; i < L1.size(); i++) {
		for (int j = 0; j < L2.size(); j++) {
			Point M1 = L1[(i + 0) % L1.size()];
			Point M2 = L1[(i + 1) % L1.size()];
			Point M3 = L2[(j + 0) % L2.size()];
			Point M4 = L2[(j + 1) % L2.size()];
			if (ShareBorder(M1, M2, M3, M4) == true) {
				return true;
			}
		}
	}
	return false;
}

int dfs(int pos, int maxval, int lim, vector<int> Current) {
	if (pos == lim) return maxval;

	// Recursion
	int Return = (1 << 30);
	for (int i = 1; i <= maxval + 1; i++) {
		bool flag = true;
		for (int j = 0; j < pos; j++) {
			if (graph[j][pos] == true && Current[j] == i) flag = false;
		}
		if (flag == false) continue;
		vector<int> New = Current;
		New.push_back(i);
		int ret = dfs(pos + 1, max(maxval, i), lim, New);
		Return = min(Return, ret);
	}
	return Return;
}

int main() {
	while (true) {
		Initialize();

		// Step 1. Input
		cin >> N; if (N == 0) break;
		for (int i = 0; i < N; i++) {
			cin >> Name[i];
			while (true) {
				int px; cin >> px; if (px == -1) break;
				int py; cin >> py;
				P[i].push_back(Point{ px, py });
			}
		}

		// Step 2. Sort by Name
		vector<string> List;
		for (int i = 0; i < N; i++) List.push_back(Name[i]);
		sort(List.begin(), List.end());
		List.erase(unique(List.begin(), List.end()), List.end());


		// Step 2. Brute Force
		for (int i = 0; i < N; i++) {
			for (int j = i + 1; j < N; j++) {
				if (Share(P[i], P[j]) == false) continue;
				int pos1 = lower_bound(List.begin(), List.end(), Name[i]) - List.begin();
				int pos2 = lower_bound(List.begin(), List.end(), Name[j]) - List.begin();
				graph[pos1][pos2] = true;
				graph[pos2][pos1] = true;
			}
		}

		// Step 3. Find Minimum Colors
		int Answer = dfs(0, 0, List.size(), vector<int>{});

		// Step 4. Output
		cout << Answer << endl;
	}
	return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 3232
final_rank: 8

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, s, t) for (int i = (int)(s); i < (int)(t); ++i)
#define revrep(i, t, s) for (int i = (int)(t)-1; i >= (int)(s); --i)
#define all(x) begin(x), end(x)
template <typename T>
bool chmax(T& a, const T& b) {
    return a < b ? (a = b, 1) : 0;
}
template <typename T>
bool chmin(T& a, const T& b) {
    return a > b ? (a = b, 1) : 0;
}

using T = double;
using Vec = std::complex<T>;

std::istream& operator>>(std::istream& is, Vec& p) {
    T x, y;
    is >> x >> y;
    p = {x, y};
    return is;
}

T dot(const Vec& a, const Vec& b) { return (std::conj(a) * b).real(); }

T cross(const Vec& a, const Vec& b) { return (std::conj(a) * b).imag(); }

const T PI = std::acos(-1);
constexpr T eps = 1e-8;
inline bool eq(T a, T b) { return std::abs(a - b) <= eps; }
inline bool eq(Vec a, Vec b) { return std::abs(a - b) <= eps; }
inline bool lt(T a, T b) { return a < b - eps; }
inline bool leq(T a, T b) { return a <= b + eps; }

struct Line {
    Vec p1, p2;
    Line() = default;
    Line(const Vec& p1, const Vec& p2) : p1(p1), p2(p2) {}
    Vec dir() const { return p2 - p1; }
};

struct Segment : Line {
    using Line::Line;
};

struct Circle {
    Vec c;
    T r;
    Circle() = default;
    Circle(const Vec& c, T r) : c(c), r(r) {}
};

using Polygon = std::vector<Vec>;

Vec rot(const Vec& a, T ang) { return a * Vec(std::cos(ang), std::sin(ang)); }

Vec perp(const Vec& a) { return Vec(-a.imag(), a.real()); }

Vec projection(const Line& l, const Vec& p) {
    return l.p1 + dot(p - l.p1, l.dir()) * l.dir() / std::norm(l.dir());
}

Vec reflection(const Line& l, const Vec& p) {
    return T(2) * projection(l, p) - p;
}

// 0: collinear
// 1: counter-clockwise
// -1: clockwise
int ccw(const Vec& a, const Vec& b, const Vec& c) {
    if (eq(cross(b - a, c - a), 0)) return 0;
    if (lt(cross(b - a, c - a), 0)) return -1;
    return 1;
}

void sort_by_arg(std::vector<Vec>& pts) {
    std::sort(pts.begin(), pts.end(), [&](auto& p, auto& q) {
        if ((p.imag() < 0) != (q.imag() < 0)) return (p.imag() < 0);
        if (cross(p, q) == 0) {
            if (p == Vec(0, 0))
                return !(q.imag() < 0 || (q.imag() == 0 && q.real() > 0));
            if (q == Vec(0, 0))
                return (p.imag() < 0 || (p.imag() == 0 && p.real() > 0));
            return (p.real() > q.real());
        }
        return (cross(p, q) > 0);
    });
}

bool intersect(const Segment& s, const Vec& p) {
    Vec u = s.p1 - p, v = s.p2 - p;
    return eq(cross(u, v), 0) && leq(dot(u, v), 0);
}

int intersect(const Segment& s, const Segment& t) {
    auto a = s.p1, b = s.p2;
    auto c = t.p1, d = t.p2;
    if (ccw(a, b, c) != ccw(a, b, d) && ccw(c, d, a) != ccw(c, d, b)) return 2;
    if (intersect(s, c) || intersect(s, d) || intersect(t, a) ||
        intersect(t, b))
        return 1;
    return 0;
}

int intersect2(const Segment& s, const Segment& t) {
    if (!intersect(s, t)) return false;
    if (!eq(cross(s.dir(), t.dir()), 0)) return false;

    auto a = s.p1, b = s.p2;
    auto c = t.p1, d = t.p2;
    if (a.real() > b.real() || (a.real() == b.real() && a.imag() > b.imag()))
        swap(a, b);
    if (c.real() > d.real() || (c.real() == d.real() && c.imag() > d.imag()))
        swap(c, d);

    if (eq(a, c) || eq(b, d)) return true;
    if (eq(a, d) || eq(b, c)) return false;
    return true;
}

bool intersect(const Polygon& poly1, const Polygon& poly2) {
    const int n = poly1.size();
    const int m = poly2.size();
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            if (intersect2(Segment(poly1[i], poly1[(i + 1) % n]),
                           Segment(poly2[j], poly2[(j + 1) % m]))) {
                return true;
            }
        }
    }
    return false;
}

int chromatic_number(std::vector<std::vector<bool>>& G) {
    int n = G.size();
    std::vector<int> neighbor(n);
    for (int i = 0; i < (int)G.size(); ++i) {
        for (int j = 0; j < (int)G.size(); ++j) {
            if (G[i][j]) neighbor[i] |= 1 << j;
        }
    }
    // number of subsets of S that are independent
    std::vector<int> ind(1 << n);
    ind[0] = 1;
    for (int S = 1; S < (1 << n); ++S) {
        int v = __builtin_ctz(S);
        // (not containing v) + (containing v)
        ind[S] = ind[S ^ (1 << v)] + ind[(S ^ (1 << v)) & ~neighbor[v]];
    }
    // number of ways to choose k subsets of S that are independent
    auto f = ind;
    for (int k = 1;; ++k) {
        // numer of ways to choose k subsets of S so that they cover S
        int g = 0;
        for (int S = 0; S < (1 << n); ++S) {
            g += (__builtin_parity(S) ? -1 : 1) * f[S];
        }
        if (g) return k;
        for (int S = 1; S < (1 << n); ++S) {
            f[S] *= ind[S];
        }
    }
}

Polygon to_ccw(std::vector<Vec> vs) {
    auto c = Vec(0, 0);
    for (auto x : vs) c += x;
    c /= vs.size();
    for (auto& x : vs) x -= c;
    sort_by_arg(vs);
    for (auto& x : vs) x += c;
    return vs;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout << fixed << setprecision(15);

    while (true) {
        int n;
        cin >> n;
        if (n == 0) break;
        map<string, int> mp;
        vector<Polygon> ter(n);
        vector<int> id(n);
        rep(i, 0, n) {
            string name;
            cin >> name;
            if (!mp.count(name)) {
                mp[name] = mp.size();
            }
            id[i] = mp[name];
            while (true) {
                int x, y;
                cin >> x;
                if (x == -1) break;
                cin >> y;
                ter[i].push_back(Vec(x, y));
            }
        }
        int c = mp.size();
        vector<vector<bool>> G(c, vector<bool>(c));
        rep(i, 0, n) rep(j, i + 1, n) {
            if (intersect(ter[i], ter[j])) {
                G[id[i]][id[j]] = G[id[j]][id[i]] = true;
            }
        }

        cout << chromatic_number(G) << "\n";
    }
}
accuracy: 1
time_ms: 20
memory_kb: 3424
final_rank: 10

code_snippet: #include<bits/stdc++.h>
#define rep(i,a,...) for(int i = (a)*(strlen(#__VA_ARGS__)!=0);i<(int)(strlen(#__VA_ARGS__)?__VA_ARGS__:(a));++i)
#define per(i,a,...) for(int i = (strlen(#__VA_ARGS__)?__VA_ARGS__:(a))-1;i>=(int)(strlen(#__VA_ARGS__)?(a):0);--i)
#define foreach(i, n) for(auto &i:(n))
#define all(x) (x).begin(), (x).end()
#define bit(x) (1ll << (x))
#define lambda(RES_TYPE, ...) (function<RES_TYPE(__VA_ARGS__)>)[&](__VA_ARGS__) -> RES_TYPE
#define method(FUNC_NAME, RES_TYPE, ...) function<RES_TYPE(__VA_ARGS__)> FUNC_NAME = lambda(RES_TYPE, __VA_ARGS__)
using namespace std;
using ll = long long;
using pii = pair<int,int>;
using pll = pair<ll,ll>;
const ll MOD = (ll)1e9+7;
//const ll MOD = 998244353;
const double EPS = 1e-8;
const int INF = (ll)1e9+7;
const ll INFLL = (ll)1e18;
template<class t>
using vvector = vector<vector<t>>;
template<class t>
using vvvector = vector<vector<vector<t>>>;
template<class t>
using priority_queuer = priority_queue<t, vector<t>, greater<t>>;
template<class t, class u> bool chmax(t &a, u b){if(a<b){a=b;return true;}return false;}
template<class t, class u> bool chmin(t &a, u b){if(a>b){a=b;return true;}return false;}
#ifdef DEBUG
#define debug(x) cout<<"LINE "<<__LINE__<<": "<<#x<<" = "<<x<<endl;
#else
#define debug(x) (void)0
#endif

namespace templates{
    ll modpow(ll x, ll b,ll mod=MOD){
        ll res = 1;
        while(b){
            if(b&1)res = res * x % mod;
            x = x * x % mod;
            b>>=1;
        }
        return res;
    }

    ll modinv(ll x){
        return modpow(x, MOD-2);
    }

    bool was_output = false;

    void print();
    template <class t> void print(const vector<t> &);
    template <class t, class u> void print(const pair<t, u> &);
    template <class t> void print(const t&);
    template <class Head, class... Tail> void print(const Head&, const Tail&...);

    template <class t> void println(const vector<vector<t>>&);
    template <class t> void println(const vector<t>&);
    template <class t> void println(const t&);
    template <class Head, class... Tail> void println(const Head&, const Tail&...);
    void println();
    void newline();

    void print(){
        return;
    }

    template <class t>
    void print(const vector<t>&x){
        for(const t&i:x)print(i);
    }
    template <class t, class u>
    void print(const pair<t,u>&p){
        print(p.first);
        print(p.second);
    }
    template <class t>
    void print(const t&x){
        if(was_output)cout<<" ";
        cout<<x;
        was_output = true;
    }
    template <class Head, class... Tail>
    void print(const Head&head,const Tail&...tail){
        print(head);
        print(tail...);
    }

    template<class t>
    void println(const vector<vector<t>>&x){
        for(vector<t> i:x)println(i);
    }
    template<class t>
    void println(const vector<t>&x){
        for(const t&i:x)print(i);
        println();
    }
    template <class t>
    void println(const t&x){
        print(x);
        println();
    }
    void println(){
        if(was_output){
            cout << endl;
            was_output = false;
        }
    }
    template <class Head, class... Tail>
    void println(const Head&head,const Tail&...tail){
        print(head);
        println(tail...);
    }

    void newline(){
        was_output = true;
        println();
    }

    template<class t>
    istream& operator>>(istream&is, vector<t>&x){
        for(auto &i:x)is >> i;
        return is;
    }

    template<class t, class u>
    istream& operator>>(istream&is, pair<t, u>&x){
        is >> x.first >> x.second;
        return is;
    }

    template<class t>
    ostream& operator<<(ostream&os, vector<t> &v){
        os << "{";
        for(t &i:v){
            os << i << ", ";
        }
        os << "}";
        return os;
    }

    template<class t = long long>
    t in(){
        t res; cin >> res; return res;
    }

    template<class t>
    vector<t> sorted(vector<t> line,function<bool(t,t)> comp=[](t a,t b){return a<b;}){
        sort(line.begin(),line.end(),comp);
        return line;
    }

    template<class t>
    vector<t> reversed(vector<t> line){
        reverse(line.begin(),line.end());
        return line;
    }
    string reversed(string str){
        reverse(str.begin(),str.end());
        return str;
    }

    long long gcd(long long a,long long b){
        while(b){
            a %= b;
            swap(a,b);
        }
        return a;
    }

    long long lcm(long long a,long long b){
        return a / gcd(a,b) * b;
    }

    class output_initializer{
    public:
        output_initializer(){
            ios::sync_with_stdio(false);
            cin.tie(nullptr);
            cout << setprecision(20);
        }
    };output_initializer OUTPUT_INITIALIZER_INSTANCE = output_initializer();
}

using namespace templates;

class Vector2{
public:
  double x;
  double y;
  Vector2():x(0), y(0){}
  Vector2(double a, double b):x(a), y(b){}
  Vector2(const Vector2 &o):x(o.x), y(o.y){}

  Vector2& operator+=(Vector2 o){x+=o.x;y+=o.y;return *this;}
  Vector2& operator-=(Vector2 o){x-=o.x;y-=o.y;return *this; }
  Vector2& operator*=(double o){x*=o;y*=o;return *this;}
  Vector2& operator/=(double o){x/=o;y/=o;return *this;}

  Vector2 operator+(Vector2 o){return Vector2(*this)+=o;}
  Vector2 operator-(Vector2 o){return Vector2(*this)-=o;}
  Vector2 operator-(){return Vector2(-x,-y);}
  Vector2 operator*(double o){return Vector2(*this)*=o;}
  Vector2 operator/(double o){return Vector2(*this)/=o;}

  double abs(){
    return sqrt(mag());
  }

  double mag(){
    return x*x+y*y;
  }

  Vector2 normal(){
    return (*this) / abs();
  }

  Vector2 rotate_90(){
    return Vector2(-y,x);
  }

  static double dot(Vector2 a,Vector2 b){
    return a.x * b.x + a.y * b.y;
  }

  static double cross(Vector2 a,Vector2 b){
    return a.x * b.y - a.y * b.x;
  }
};

double dot(Vector2 x, Vector2 y){
  return Vector2::dot(x,y);
}

double cross(Vector2 x, Vector2 y){
  return Vector2::cross(x,y);
}

void output(Vector2 x){
  cout << x.x << " " << x.y << endl;
}

bool operator==(Vector2 a,Vector2 b){
    return (a-b).abs() <= EPS;
}


class Line{
public:
  Vector2 f;
  Vector2 s;
  Line(){
    f = Vector2(0,0);
    s = Vector2(1,0);
  }
  Line(Vector2 a,Vector2 b):f(a),s(b){}

  Vector2 projection(Vector2 pos){
    pos -= f;
    Vector2 n = (s-f).normal();
    Vector2 res;
    res = n * dot(pos,n) + f;
    return res;
  }

  Vector2 reflection(Vector2 pos){
    Vector2 n;
    Vector2 res;
    pos -= f;
    double x = f.y-s.y;
    double y = s.x-f.x;
    n = Vector2(x,y).normal();
    res = pos + f - n * dot(pos,n) * 2;
    return res;
  }

  static bool is_cross(Line a,Line b){
    Vector2 ap = a.s - a.f;
    Vector2 bp = b.s - b.f;
    if(abs(Vector2::dot(ap,bp))<EPS){
      return true;
    }
    return false;
  }

  static bool is_cross_segment(Line a,Line b){
    if(Line::is_parallel(a,b)){
      return a.is_online(b.f) || a.is_online(b.s) || b.is_online(a.f) || b.is_online(a.s);
    }
	  if(Vector2::cross(a.s-a.f,b.f-a.f)*Vector2::cross(a.s-a.f,b.s-a.f)>EPS)return false;
	  if(Vector2::cross(b.s-b.f,a.f-b.f)*Vector2::cross(b.s-b.f,a.s-b.f)>EPS)return false;
return true;
  }

  static bool is_parallel(Line a,Line b){
    Vector2 ap = a.s - a.f;
    Vector2 bp = b.s - b.f;
    if(abs(Vector2::cross(ap,bp))<EPS){
      return true;
    }
    return false;
  }

  static Vector2 cul_cross(Line a,Line b){
    if(a.is_online(b.f)){
      return b.f;
    }
    if(a.is_online(b.s)){
      return b.s;
    }
    if(b.is_online(a.f)){
      return a.f;
    }
    if(b.is_online(a.s)){
      return a.s;
    }
    Vector2 an = (a.s-a.f).rotate_90();
    if(Vector2::dot(b.f-a.f,an)<0){
      an = -an;
    }
    double dis_1 = Vector2::dot(b.f-a.f,an);
    double dis_2 = Vector2::dot(b.s-a.f,an);
    return b.f + (b.s - b.f) * dis_1 / (dis_1 - dis_2);
  }

  double cul_distance(Vector2 pos){
    Vector2 v = s-f;
    Vector2 n = v.normal();
    double k = v.abs();
    pos -= f;
    double dis = Vector2::dot(pos,n);
    if(dis>0){
      if(dis>k)dis=k;
      pos -= n * dis;
    }
    return pos.abs();
  }

  static double cul_distance(Line a,Line b){
    if(Line::is_cross_segment(a,b))return 0;
    return min({a.cul_distance(b.f),a.cul_distance(b.s),
          b.cul_distance(a.f),b.cul_distance(a.s)});
  }

  bool is_online(Vector2 pos){
    return cul_distance(pos) < EPS;
  }
};

Vector2 input_vector2(){
	double x,y;
	cin >> x >> y;
	return Vector2(x,y);
}

int func(int n){
    map<string,vvector<Vector2>> countries_;
    rep(_,n){
        string name = in<string>();
        vector<Vector2> territory;
        while(true){
            int x = in();
            if(x < 0)break;
            int y = in();
            territory.emplace_back(x,y);
        }
        countries_[name].emplace_back(territory);
    }
    int N = countries_.size();
    vvvector<Vector2> countries;
    foreach(c,countries_)countries.emplace_back(c.second);
    vvector<int> touches(N,vector<int>(N,false));
    method(isTouch2,bool,vector<Vector2> a,vector<Vector2> b){
        int n = a.size();
        int m = b.size();
        rep(i,n){
            rep(j,m){
                int i2 = (i + 1) % n;
                int j2 = (j + 1) % m;
                Line al(a[i],a[i2]);
                Line bl(b[j],b[j2]);
                if(not Line::is_parallel(al,bl))continue;
                if(not Line::is_cross_segment(al,bl))continue;
                Vector2 from = al.f;
                Vector2 to = al.s;
                Vector2 o = from;
                Vector2 n = (to - from).normal();
                vector<double> as;
                vector<double> bs;
                as.emplace_back(dot(al.f-o,n));
                as.emplace_back(dot(al.s-o,n));
                bs.emplace_back(dot(bl.f-o,n));
                bs.emplace_back(dot(bl.s-o,n));
                sort(all(as));
                sort(all(bs));
                if(max(as[0],bs[0]) + EPS < min(as[1],bs[1]))return true;
            }
        }
        return false;
    };
    method(isTouch,bool,vvector<Vector2> a,vvector<Vector2> b){
        foreach(i,a)foreach(j,b)if(isTouch2(i,j))return true;
        return false;
    };
    rep(i,N){
        rep(j,N){
            if(i==j)continue;
            touches[i][j] = isTouch(countries[i],countries[j]);
        }
    }
    vector<map<vector<int>,int>> dp(N);
    vector<int> colors;
    method(rec,int,int p){
        if(p==N){
            int res = 0;
            foreach(i,colors)chmax(res,i);
            return res + 1;
        }
        if(dp[p].count(colors))return dp[p][colors];
        int &it = dp[p][colors];
        it = INF;
        int maxs = 0;
        rep(i,p)chmax(maxs,colors[i]);
        rep(color,maxs+2){
            bool can = true;
            rep(i,p){
                if(colors[i]==color and touches[i][p])can = false;
            }
            if(not can)continue;
            colors.emplace_back(color);
            chmin(it,rec(p+1));
            colors.pop_back();
        }
        return it;
    };
    return rec(0);
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    while(true){
        int n = in();
        if(n==0)break;
        println(func(n));
    }
    return 0;
}
accuracy: 1
time_ms: 70
memory_kb: 21200
final_rank: 20

code_snippet: #line 2 "library/bits/stdc++.h"

// C
#ifndef _GLIBCXX_NO_ASSERT
#include <cassert>
#endif
#include <cctype>
#include <cerrno>
#include <cfloat>
#include <ciso646>
#include <climits>
#include <clocale>
#include <cmath>
#include <csetjmp>
#include <csignal>
#include <cstdarg>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>

#if __cplusplus >= 201103L
#include <ccomplex>
#include <cfenv>
#include <cinttypes>
#include <cstdbool>
#include <cstdint>
#include <ctgmath>
#include <cwchar>
#include <cwctype>
#endif

// C++
#include <algorithm>
#include <bitset>
#include <complex>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <iterator>
#include <limits>
#include <list>
#include <locale>
#include <map>
#include <memory>
#include <new>
#include <numeric>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <typeinfo>
#include <utility>
#include <valarray>
#include <vector>

#if __cplusplus >= 201103L
#include <array>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <forward_list>
#include <future>
#include <initializer_list>
#include <mutex>
#include <random>
#include <ratio>
#include <regex>
#include <scoped_allocator>
#include <system_error>
#include <thread>
#include <tuple>
#include <typeindex>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#endif
#line 2 "Source.cpp"
using namespace std;

typedef string::const_iterator State;
#define eps 1e-8L
#define MAX_MOD 1000000007LL
#define GYAKU 500000004LL
#define MOD 998244353LL
#define pb push_back
#define mp make_pair
typedef long long ll;
typedef long double ld;
#define REP(a, b) for (long long(a) = 0; (a) < (b); ++(a))
#define ALL(x) (x).begin(), (x).end()

#line 2 "library/kotamanegi/geometry.hpp"
using namespace std;

typedef long double ld;
typedef complex<ld> Point;
typedef pair<Point, Point> Segment;
typedef Segment Line;

const ld EPS = 1e-10;

ld abs(Line x)
{
  return abs(x.second - x.first);
}

// dot = |a||b| cos(theta)
ld dot(Point a, Point b)
{
  return a.real() * b.real() + a.imag() * b.imag();
}

// cross = |a||b| sin(theta)
ld cross(Point a, Point b)
{
  return a.real() * b.imag() - a.imag() * b.real();
}

// 射影: 直線sと点pから引いた垂線の交点
Point projection(Line s, Point p)
{
  Point base = s.second - s.first;
  ld r = dot(p - s.first, base) / norm(base);
  return s.first + base * r;
}

// 反射: 点pを直線sと線対称に移動した点
Point reflection(Line s, Point p)
{
  return p + (projection(s, p) - p) * Point{2.0, 0.0};
}

enum class Position
{
  COUNTER_CLOCKWISE, // 反時計回り
  CLOCKWISE,         // 時計回り
  ON_LINE_BACK,      // Lineの始点側に点がある
  ON_LINE_FRONT,     // Lineの終点側に点がある
  ON_SEGMENT         // Line上に点がある
};

// ccw: 線分sと点pの位置関係を示す
Position ccw(Line s, Point p)
{
  Point a = s.second - s.first, b = p - s.first;
  if (cross(a, b) > eps)
    return Position::COUNTER_CLOCKWISE;
  if (cross(a, b) < -eps)
    return Position::CLOCKWISE;
  if (dot(a, b) < -EPS)
    return Position::ON_LINE_BACK;
  if (norm(a) < norm(b))
    return Position::ON_LINE_FRONT;
  return Position::ON_SEGMENT;
}

// 平行判定
bool is_parallel(Point a, Point b) { return abs(cross(a, b)) < EPS; }
bool is_parallel(Line a, Line b) { return is_parallel(a.second - a.first, b.second - b.first); }

// 直交判定
bool is_orthogonal(Point a, Point b) { return abs(dot(a, b)) < EPS; }
bool is_orthogonal(Line a, Line b) { return is_orthogonal(a.second - a.first, b.second - b.first); }

// 交差判定
bool is_intersect(Segment s1, Segment s2)
{
  for (int i = 0; i < 2; ++i)
  {
    if (cross(s1.second - s1.first, s2.first - s1.first) * cross(s1.second - s1.first, s2.second - s1.first) > eps)
    {
      return false;
    }
    if (ccw(s1, s2.first) == Position::ON_LINE_BACK and ccw(s1, s2.second) == Position::ON_LINE_BACK)
    {
      return false;
    }
    if (ccw(s1, s2.first) == Position::ON_LINE_FRONT and ccw(s1, s2.second) == Position::ON_LINE_FRONT)
    {
      return false;
    }
    swap(s1, s2);
  }
  return true;
}

// 交差点
Point intersection_point(Line s1, Line s2)
{
  assert(!is_parallel(s1, s2));
  ld s, t;
  ld deno = cross(s1.second - s1.first, s2.second - s2.first);
  s = cross(s2.first - s1.first, s2.second - s2.first) / deno;
  return s1.first + s * (s1.second - s1.first);
}

ld distance(Segment s, Point p)
{
  if (dot(s.second - s.first, p - s.first) < eps)
    return abs(p - s.first);
  if (dot(s.first - s.second, p - s.second) < eps)
    return abs(p - s.second);
  return abs(cross(s.second - s.first, p - s.first)) / abs(s);
}

ld distance(Segment s1, Segment s2)
{
  if (is_intersect(s1, s2))
  {
    return 0;
  }
  return min({distance(s1, s2.first), distance(s1, s2.second), distance(s2, s1.first), distance(s2, s1.second)});
}

typedef vector<Point> Polygon;

ld area(Polygon poly)
{
  ld ans = 0;
  for (int i = 0; i < poly.size(); ++i)
  {
    Point a = poly[i], b = poly[(i + 1) % poly.size()];
    ans += cross(a, b);
  }
  ans /= 2.0L;
  return abs(ans);
}

bool is_convex(Polygon poly)
{
  for (int i = 0; i < poly.size(); ++i)
  {
    Line a = {poly[i], poly[(i + 1) % poly.size()]};
    if (ccw(a, poly[(i + 2) % poly.size()]) == Position::CLOCKWISE)
      return false;
  }
  return true;
}

bool is_on_polygon(Polygon poly, Point p)
{
  for (int i = 0; i < poly.size(); ++i)
  {
    Line a = {poly[i], poly[(i + 1) % poly.size()]};
    if (ccw(a, p) == Position::ON_SEGMENT)
      return true;
  }
  return false;
}

bool is_inside_polygon(Polygon poly, Point p)
{
  if (is_on_polygon(poly, p))
    return true;
  const int n = poly.size();
  int wn = 0;
  for (int i = 0; i < n; ++i)
  {
    ld vt = (p.imag() - poly[i].imag()) / (poly[(i + 1) % n].imag() - poly[i].imag());
    if (p.real() < (poly[i].real() + vt * (poly[(i + 1) % n].real() - poly[i].real())))
    {
      if ((poly[i].imag() <= p.imag()) and (poly[(i + 1) % n].imag() > p.imag()))
      {
        wn++;
      }
      else if ((poly[i].imag() > p.imag()) and (poly[(i + 1) % n].imag() <= p.imag()))
      {
        wn--;
      }
    }
  }
  if (wn != 0)
    return true;
  return false;
}
#line 17 "Source.cpp"

#define int long long
int dp[(1 << 10)];
void solve()
{
  int n;
  cin >> n;
  if (n == 0)
    exit(0);

  map<string, vector<Polygon>> inputs;
  REP(i, n)
  {
    string s;
    cin >> s;
    Polygon poly;
    while (true)
    {
      int a;
      cin >> a;
      if (a == -1)
        break;
      int b;
      cin >> b;
      poly.push_back({(ld)a, (ld)b});
    }
    inputs[s].push_back(poly);
  }
  set<pair<string, string>> edges;
  vector<string> targets;
  for (auto x : inputs)
  {
    targets.push_back(x.first);
    for (auto y : inputs)
    {
      if (x.first == y.first)
        continue;
      int required_push = 0;
      for (auto poly1 : x.second)
      {
        for (auto poly2 : y.second)
        {
          REP(t, poly1.size())
          {
            REP(p, poly2.size())
            {
              if (is_intersect({poly1[t], poly1[(t + 1) % poly1.size()]}, {poly2[p], poly2[(p + 1) % poly2.size()]}))
              {
                if (poly1[t] == poly2[p])
                {
                  Line a = {poly1[t], poly1[(t + 1) % poly1.size()]};
                  Line b = {poly2[p], poly2[(p + 1) % poly2.size()]};
                  if (abs(dot(a.second - a.first, b.second - b.first) - abs(a.second - a.first) * abs(b.second - b.first)) > eps)
                  {
                    continue;
                  }
                }
                if (poly1[(t + 1) % poly1.size()] == poly2[p])
                {
                  Line a = {poly1[(t + 1) % poly1.size()], poly1[t]};
                  Line b = {poly2[p], poly2[(p + 1) % poly2.size()]};
                  if (abs(dot(a.second - a.first, b.second - b.first) - abs(a.second - a.first) * abs(b.second - b.first)) > eps)
                  {
                    continue;
                  }
                }
                if (poly1[t] == poly2[(p + 1) % poly2.size()])
                {
                  Line a = {poly1[t], poly1[(t + 1) % poly1.size()]};
                  Line b = {poly2[(p + 1) % poly2.size()], poly2[p]};
                  if (abs(dot(a.second - a.first, b.second - b.first) - abs(a.second - a.first) * abs(b.second - b.first)) > eps)
                  {
                    continue;
                  }
                }
                if (poly1[(t + 1) % poly1.size()] == poly2[(p + 1) % poly2.size()])
                {
                  Line a = {poly1[(t + 1) % poly1.size()], poly1[t]};
                  Line b = {poly2[(p + 1) % poly2.size()], poly2[p]};
                  if (abs(dot(a.second - a.first, b.second - b.first) - abs(a.second - a.first) * abs(b.second - b.first)) > eps)
                  {
                    continue;
                  }
                }
                required_push = 1;
              }
            }
          }
        }
      }
      if (required_push)
      {
        edges.insert(pair<string, string>{x.first, y.first});
      }
    }
  }
  REP(i, (1 << targets.size()))
  {
    dp[i] = 1e9;
  }
  dp[0] = 0;
  REP(i, (1 << targets.size()))
  {
    REP(q, (1 << targets.size()))
    {
      if (i & q)
        continue;
      vector<string> can_kouho;
      REP(j, targets.size())
      {
        if (q & (1 << j))
        {
          can_kouho.push_back(targets[j]);
        }
      }
      int bad = 0;
      for (auto x : can_kouho)
      {
        for (auto y : can_kouho)
        {
          if (edges.count({x, y}))
          {
            bad = 1;
            break;
          }
        }
      }
      if (!bad)
      {
        dp[i | q] = min(dp[i | q], dp[i] + 1);
      }
    }
  }
  cout << dp[(1 << targets.size()) - 1] << endl;
}
#undef int

// generated by oj-template v4.7.2
// (https://github.com/online-judge-tools/template-generator)
int main()
{
  // Fasterize input/output script
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  cout << fixed << setprecision(100);
  // scanf/printf user should delete this fasterize input/output script

  int t = 100000;
  // cin >> t; // comment out if solving multi testcase
  for (int testCase = 1; testCase <= t; ++testCase)
  {
    solve();
  }
  return 0;
}
accuracy: 1
time_ms: 250
memory_kb: 3408
final_rank: 18

code_snippet: #pragma GCC ta g("avx2")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
#include <bits/stdc++.h>
using namespace std;
#define DEBUG
#ifdef DEBUG
template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << '(' << p.first << ',' << p.second << ')';
    return os;
}
template <class T> ostream &operator<<(ostream &os, const vector<T> &v) {
    os << '{';
    for(int i = 0; i < (int)v.size(); i++) {
        if(i) { os << ','; }
        os << v[i];
    }
    os << '}';
    return os;
}
void debugg() { cerr << endl; }
template <class T, class... Args>
void debugg(const T &x, const Args &... args) {
    cerr << " " << x;
    debugg(args...);
}
#define debug(...)                                                             \
    cerr << __LINE__ << " [" << #__VA_ARGS__ << "]: ", debugg(__VA_ARGS__)
#define dump(x) cerr << __LINE__ << " " << #x << " = " << (x) << endl
#else
#define debug(...) (void(0))
#define dump(x) (void(0))
#endif
using namespace std;
typedef long long ll;
typedef vector<ll> vl;
typedef vector<vl> vvl;
typedef vector<char> vc;
typedef vector<string> vs;
typedef vector<bool> vb;
typedef vector<double> vd;
typedef pair<ll,ll> P;
typedef pair<int,int> pii;
typedef vector<P> vpl;
typedef tuple<ll,ll,ll> tapu;
#define rep(i,n) for(int i=0; i<(n); i++)
#define REP(i,a,b) for(int i=(a); i<(b); i++)
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
template<typename T1,typename T2> inline bool chmin(T1 &a,T2 b){
	if(a>b){
		a = b; return true;
	}
	else return false;
}
template<typename T1,typename T2> inline bool chmax(T1 &a,T2 b){
	if(a<b){
		a = b; return true;
	}
	else return false;
}
template<typename T> inline void print(T &a){
    int sz = a.size();
    for(auto itr = a.begin(); itr != a.end(); itr++){
		cout << *itr;
        sz--;
        if(sz) cout << " ";
	}
    cout << "\n";
}
template<typename T1,typename T2> inline void print2(T1 a, T2 b){
	cout << a << " " << b << "\n";
}
template<typename T1,typename T2,typename T3> inline void print3(T1 a, T2 b, T3 c){
	cout << a << " " << b << " " << c << "\n";
}
void mark() {cout << "#" << "\n";}
ll pcount(ll x) {return __builtin_popcountll(x);}
//#include <atcoder/maxflow>
//using namespace atcoder;
const int inf = (1<<30)-1;
const ll linf = 1LL<<61;
const int MAX = 510000;
int dy[8] = {0,1,0,-1,1,-1,-1,1};
int dx[8] = {-1,0,1,0,1,-1,1,-1};
const double pi = acos(-1);
const double eps = 1e-9;
const int mod = 1e9 + 7;
//const int mod = 998244353;

namespace geometry {
    // Point : 複素数型を位置ベクトルとして扱う
    // 実軸(real)をx軸、挙軸(imag)をy軸として見る
    using D = double;
    using Point = complex<D>;
    const D EPS = 1e-7;
    const D PI = acosl(-1);

    inline bool equal(const D &a, const D &b) { return fabs(a - b) < EPS; }

    // 単位ベクトル(unit vector)を求める
    Point unitVector(const Point &a) { return a / abs(a); }

    // 法線ベクトル(normal vector)を求める
    // 90度回転した単位ベクトルをかける
    // -90度がよければPoint(0, -1)をかける
    Point normalVector(const Point &a) { return a * Point(0, 1); }

    // 内積(dot product) : a・b = |a||b|cosΘ
    D dot(const Point &a, const Point &b) {
        return (a.real() * b.real() + a.imag() * b.imag());
    }

    // 外積(cross product) : a×b = |a||b|sinΘ
    D cross(const Point &a, const Point &b) {
        return (a.real() * b.imag() - a.imag() * b.real());
    }

    // 点pを反時計回りにtheta度回転
    // thetaはラジアン！！！
    Point rotate(const Point &p, const D &theta) {
        return Point(cos(theta) * p.real() - sin(theta) * p.imag(),
                     sin(theta) * p.real() + cos(theta) * p.imag());
    }

    // ラジアン->度
    D radianToDegree(const D &radian) { return radian * 180.0 / PI; }

    // 度->ラジアン
    D degreeToRadian(const D &degree) { return degree * PI / 180.0; }

    // 点の回転方向
    // 点a, b, cの位置関係について(aが基準点)
    int ccw(const Point &a, Point b, Point c) {
        b -= a, c -= a;
        // 点a, b, c が
        // 反時計回りの時、
        if(cross(b, c) > EPS) return 1;
        // 時計回りの時、
        if(cross(b, c) < -EPS) return -1;
        // c, a, bがこの順番で同一直線上にある時、
        if(dot(b, c) < 0) return 2;
        // a, b, cがこの順番で同一直線上にある場合、
        if(norm(b) < norm(c)) return -2;
        // cが線分ab上にある場合、
        return 0;
    }

    // Line : 直線を表す構造体
    // b - a で直線・線分を表せる
    struct Line {
        Point a, b;
        Line() = default;
        Line(Point a, Point b) : a(a), b(b) {}
        // Ax+By=C
        Line(D A, D B, D C) {
            if(equal(A, 0)) {
                a = Point(0, C / B), b = Point(1, C / B);
            } else if(equal(B, 0)) {
                b = Point(C / A, 0), b = Point(C / A, 1);
            } else {
                a = Point(0, C / B), b = Point(C / A, 0);
            }
        }
    };

    // Segment : 線分を表す構造体
    // Lineと同じ
    struct Segment : Line {
        Segment() = default;

        Segment(Point a, Point b) : Line(a, b) {}
    };

    // Circle : 円を表す構造体
    // pが中心の位置ベクトル、rは半径
    struct Circle {
        Point p;
        D r;

        Circle() = default;

        Circle(Point p, D r) : p(p), r(r) {}
    };

    // 2直線の直交判定 : a⊥b <=> dot(a, b) = 0
    bool isOrthogonal(const Line &a, const Line &b) {
        return equal(dot(a.b - a.a, b.b - b.a), 0);
    }
    // 2直線の平行判定 : a//b <=> cross(a, b) = 0
    bool isParallel(const Line &a, const Line &b) {
        return equal(cross(a.b - a.a, b.b - b.a), 0);
    }

    // 点cが直線ab上にあるか
    bool isPointOnLine(const Point &a, const Point &b, const Point &c) {
        return isParallel(Line(a, b), Line(a, c));
    }

    // 点cが"線分"ab上にあるか
    bool isPointOnSegment(const Point &a, const Point &b, const Point &c) {
        // |a-c| + |c-b| <= |a-b| なら線分上
        return (abs(a - c) + abs(c - b) < abs(a - b) + EPS);
    }

    // 直線lと点pの距離を求める
    D distanceBetweenLineAndPoint(const Line &l, const Point &p) {
        return abs(cross(l.b - l.a, p - l.a)) / abs(l.b - l.a);
    }

    // 線分lと点pの距離を求める
    // 定義：点pから「線分lのどこか」への最短距離
    D distanceBetweenSegmentAndPoint(const Segment &l, const Point &p) {
        if(dot(l.b - l.a, p - l.a) < EPS) return abs(p - l.a);
        if(dot(l.a - l.b, p - l.b) < EPS) return abs(p - l.b);
        return abs(cross(l.b - l.a, p - l.a)) / abs(l.b - l.a);
    }

    // 直線s, tの交点の計算
    Point crossPoint(const Line &s, const Line &t) {
        D d1 = cross(s.b - s.a, t.b - t.a);
        D d2 = cross(s.b - s.a, s.b - t.a);
        if(equal(abs(d1), 0) && equal(abs(d2), 0)) return t.a;
        return t.a + (t.b - t.a) * (d2 / d1);
    }

    // 線分s, tの交点の計算
    Point crossPoint(const Segment &s, const Segment &t) {
        return crossPoint(Line(s), Line(t));
    }

    // 線分sと線分tが交差しているかどうか
    // bound:線分の端点を含むか
    bool isIntersect(const Segment &s, const Segment &t, bool bound) {
        return ccw(s.a, s.b, t.a) * ccw(s.a, s.b, t.b) < bound &&
               ccw(t.a, t.b, s.a) * ccw(t.a, t.b, s.b) < bound;
    }

    // 線分sとtの距離
    D distanceBetweenSegments(const Segment &s, const Segment &t) {
        if(isIntersect(s, t, 1)) return (D)(0);
        D ans = distanceBetweenSegmentAndPoint(s, t.a);
        chmin(ans, distanceBetweenSegmentAndPoint(s, t.b));
        chmin(ans, distanceBetweenSegmentAndPoint(t, s.a));
        chmin(ans, distanceBetweenSegmentAndPoint(t, s.b));
        return ans;
    }

    // 射影(projection)
    // 直線(線分)lに点pから引いた垂線の足を求める
    Point projection(const Line &l, const Point &p) {
        D t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
        return l.a + (l.a - l.b) * t;
    }

    Point projection(const Segment &l, const Point &p) {
        D t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
        return l.a + (l.a - l.b) * t;
    }

    // 反射(reflection)
    // 直線lを対称軸として点pと線対称の位置にある点を求める
    Point reflection(const Line &l, const Point &p) {
        return p + (projection(l, p) - p) * (D)2.0;
    }

    // 2つの円の交差判定
    // 返り値は共通接線の数
    int isIntersect(const Circle &c1, const Circle &c2) {
        D d = abs(c1.p - c2.p);
        // 2つの円が離れている場合
        if(d > c1.r + c2.r + EPS) return 4;
        // 外接している場合
        if(equal(d, c1.r + c2.r)) return 3;
        // 内接している場合
        if(equal(d, abs(c1.r - c2.r))) return 1;
        // 内包している場合
        if(d < abs(c1.r - c2.r) - EPS) return 0;
        return 2;
    }

    // 2つの円の交点
    vector<Point> crossPoint(const Circle &c1, const Circle &c2) {
        vector<Point> res;
        int mode = isIntersect(c1, c2);
        // 2つの中心の距離
        D d = abs(c1.p - c2.p);
        // 2円が離れている場合
        if(mode == 4) return res;
        // 1つの円がもう1つの円に内包されている場合
        if(mode == 0) return res;
        // 2円が外接する場合
        if(mode == 3) {
            D t = c1.r / (c1.r + c2.r);
            res.emplace_back(c1.p + (c2.p - c1.p) * t);
            return res;
        }
        // 内接している場合
        if(mode == 1) {
            if(c2.r < c1.r - EPS) {
                res.emplace_back(c1.p + (c2.p - c1.p) * (c1.r / d));
            } else {
                res.emplace_back(c2.p + (c1.p - c2.p) * (c2.r / d));
            }
            return res;
        }
        // 2円が重なる場合
        D rc1 = (c1.r * c1.r + d * d - c2.r * c2.r) / (2 * d);
        D rs1 = sqrt(c1.r * c1.r - rc1 * rc1);
        if(c1.r - abs(rc1) < EPS) rs1 = 0;
        Point e12 = (c2.p - c1.p) / abs(c2.p - c1.p);
        res.emplace_back(c1.p + rc1 * e12 + rs1 * e12 * Point(0, 1));
        res.emplace_back(c1.p + rc1 * e12 + rs1 * e12 * Point(0, -1));
        return res;
    }

    // 点pが円cの内部(円周上も含む)に入っているかどうか
    bool isInCircle(const Circle &c, const Point &p) {
        D d = abs(c.p - p);
        return (equal(d, c.r) || d < c.r - EPS);
    }

    // 円cと直線lの交点
    vector<Point> crossPoint(const Circle &c, const Line &l) {
        vector<Point> res;
        D d = distanceBetweenLineAndPoint(l, c.p);
        // 交点を持たない
        if(d > c.r + EPS) return res;
        // 接する
        Point h = projection(l, c.p);
        if(equal(d, c.r)) {
            res.emplace_back(h);
            return res;
        }
        Point e = unitVector(l.b - l.a);
        D ph = sqrt(c.r * c.r - d * d);
        res.emplace_back(h - e * ph);
        res.emplace_back(h + e * ph);
        return res;
    }

    // 点pを通る円cの接線
    // 2本あるので、接点のみを返す
    vector<Point> tangentToCircle(const Point &p, const Circle &c) {
        return crossPoint(c, Circle(p, sqrt(norm(c.p - p) - c.r * c.r)));
    }

    // 円の共通接線
    vector<Line> tangent(const Circle &a, const Circle &b) {
        vector<Line> ret;
        // 2円の中心間の距離
        D g = abs(a.p - b.p);
        // 円が内包されている場合
        if(equal(g, 0)) return ret;
        Point u = unitVector(b.p - a.p);
        Point v = rotate(u, PI / 2);
        for(int s : {-1, 1}) {
            D h = (a.r + b.r * s) / g;
            if(equal(h * h, 1)) {
                ret.emplace_back(a.p + (h > 0 ? u : -u) * a.r,
                                 a.p + (h > 0 ? u : -u) * a.r + v);

            } else if(1 - h * h > 0) {
                Point U = u * h, V = v * sqrt(1 - h * h);
                ret.emplace_back(a.p + (U + V) * a.r,
                                 b.p - (U + V) * (b.r * s));
                ret.emplace_back(a.p + (U - V) * a.r,
                                 b.p - (U - V) * (b.r * s));
            }
        }
        return ret;
    }

    // 多角形の面積を求める
    D PolygonArea(const vector<Point> &p) {
        D res = 0;
        int n = p.size();
        for(int i = 0; i < n - 1; i++) res += cross(p[i], p[i + 1]);
        res += cross(p[n - 1], p[0]);
        return res * 0.5;
    }

    // 凸多角形かどうか
    bool isConvex(const vector<Point> &p) {
        int n = p.size();
        int now, pre, nxt;
        for(int i = 0; i < n; i++) {
            pre = (i - 1 + n) % n;
            nxt = (i + 1) % n;
            now = i;
            if(ccw(p[pre], p[now], p[nxt]) == -1) return false;
        }
        return true;
    }

    // 凸包 O(NlogN)
    vector<Point> ConvexHull(vector<Point> &p) {
        int n = (int)p.size(), k = 0;
        sort(all(p), [](const Point &a, const Point &b) {
            return (real(a) != real(b) ? real(a) < real(b) : imag(a) < imag(b));
        });
        vector<Point> ch(2 * n);
        // 一直線上の3点を含める -> (< -EPS)
        // 含め無い -> (< EPS)
        for(int i = 0; i < n; ch[k++] = p[i++]) { // lower
            while(k >= 2 &&
                  cross(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1]) < EPS)
                --k;
        }
        for(int i = n - 2, t = k + 1; i >= 0; ch[k++] = p[i--]) { // upper
            while(k >= t &&
                  cross(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1]) < EPS)
                --k;
        }
        ch.resize(k - 1);
        return ch;
    }

    // 多角形gに点pが含まれているか?
    // 含まれる:2, 辺上にある:1, 含まれない:0
    int isContained(const vector<Point> &g, const Point &p) {
        bool in = false;
        int n = (int)g.size();
        for(int i = 0; i < n; i++) {
            Point a = g[i] - p, b = g[(i + 1) % n] - p;
            if(imag(a) > imag(b)) swap(a, b);
            if(imag(a) <= EPS && EPS < imag(b) && cross(a, b) < -EPS) in = !in;
            if(cross(a, b) == 0 && dot(a, b) <= 0) return 1;
        }
        return (in ? 2 : 0);
    }

    vector<Point> convex_cut(const vector<Point> &g, Line l){
        vector<Point> ret;
        for(int i=0; i<g.size(); i++){
            Point now = g[i], nxt = g[(i+1)%g.size()];
            if(ccw(l.a,l.b,now) != -1) ret.push_back(now);
            if(ccw(l.a,l.b,now) * ccw(l.a,l.b,nxt) < 0){
                ret.push_back(crossPoint(Line(now,nxt),l));
            }
        }
        return ret;
    }

    bool equal (const Point &a, const Point &b) {
        return equal(a.real(),b.real()) && equal(a.imag(),b.imag());
    }
} // namespace geometry

using namespace geometry;

int main(){
    while(1){
        int n; cin >> n;
        if(!n) break;
        vs s(n);
        vector<vector<Point>> polys(n);
        rep(i,n){
            cin >> s[i];
            vector<Point> p;
            int x,y;
            while(1){
                cin >> x;
                if(x == -1) break;
                cin >> y;
                p.emplace_back(x,y);
            }
            polys[i] = p;
        }
        vector<vb> insec(n,vb(n,false));
        map<string,int> mp;
        int k = 0;
        rep(i,n){
            if(mp.count(s[i])) continue;
            mp[s[i]] = k++;
        }
        vvl G(k,vl(k,0));
        rep(i,n) rep(j,n){
            if(s[i] == s[j]) continue;
            int u = mp[s[i]], v = mp[s[j]];
            int si = polys[i].size(), sj = polys[j].size();
            bool f = false;
            rep(l,si){
                rep(m,sj){
                    auto s1 = Segment(polys[i][l],polys[i][(l+1)%si]);
                    auto s2 = Segment(polys[j][m],polys[j][(m+1)%sj]);
                    if(!isParallel(s1,s2)) continue;
                    if(equal(s1.a,s2.a) && equal(s1.b,s2.b)){
                        f = true; break;
                    }
                    if(equal(s1.a,s2.b) && equal(s1.b,s2.a)){
                        f = true; break;
                    }
                    if(s2.a!=s1.a && s2.a!=s1.b && ccw(s1.a,s1.b,s2.a)==0){
                        f = true; break;
                    }
                    if(s2.b!=s1.a && s2.b!=s1.b && ccw(s1.a,s1.b,s2.b)==0){
                        f = true; break;
                    }
                }
                if(f) break;
            }
            if(f){
                G[u][v] = G[v][u] = 1;
            }
        }
        vb ok(1<<k,true);
        rep(bit,1<<k){
            rep(i,k) rep(j,k){
                if((bit>>i&1) && (bit>>j&1) && G[i][j]==1){
                    ok[bit] = false;
                }
            }
        }
        vl dp(1<<k,inf);
        dp[0] = 0;
        rep(bit,1<<k){
            int S = (1<<k)-1-bit;
            for(int T=S; T>=0; T--){
                T &= S;
                if(ok[T]) chmin(dp[bit|T], dp[bit] + 1);
            }
        }
        cout << dp[(1<<k)-1] << "\n";
    }
}
accuracy: 1
time_ms: 10
memory_kb: 3400
final_rank: 5

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

void solve(int n) {
    vector<int> idx(n), m(n);
    vector<vector<pair<int, int>>> poly(n);
    map<string, int> mp;
    for (int i = 0; i < n; i++) {
        string S;
        cin >> S;
        if (!mp.count(S)) mp[S] = mp.size();
        idx[i] = mp[S];
        int x, y;
        while (cin >> x, x >= 0) {
            cin >> y;
            poly[i].emplace_back(x, y);
        }
        m[i] = poly[i].size();
    }

    auto check = [&](pair<int, int> a, pair<int, int> b, pair<int, int> c, pair<int, int> d) {
        if (a.first > b.first) swap(a, b);
        if (c.first > d.first) swap(c, d);
        pair<int, int> diag1, diag2;
        {
            int dx = b.first - a.first, dy = b.second - a.second;
            if (dx == 0) dy = 1;
            int g = gcd(dx, dy);
            diag1 = {dx / g, dy / g};
        }
        {
            int dx = d.first - c.first, dy = d.second - c.second;
            if (dx == 0) dy = 1;
            int g = gcd(dx, dy);
            diag2 = {dx / g, dy / g};
        }
        if (diag1 != diag2) return false;
        if (diag1.first == 0) {
            if (a.first != c.first) return false;
            if (a.second > b.second) swap(a, b);
            if (c.second > d.second) swap(c, d);
            return max(a.second, c.second) < min(b.second, d.second);
        }
        double y1 = a.second - double(a.first) / double(diag1.first) * diag1.second,
               y2 = c.second - double(c.first) / double(diag2.first) * diag2.second;
        if (y1 != y2) return false;
        return max(a.first, c.first) < min(b.first, d.first);
    };

    int N = mp.size();
    vector<vector<bool>> adj(N, vector<bool>(N, false));
    for (int i = 0; i < n; i++) {
        for (int j = i + 1; j < n; j++) {
            for (int k = 0; k < m[i]; k++) {
                for (int l = 0; l < m[j]; l++) {
                    if (check(poly[i][k], poly[i][(k + 1) % m[i]], poly[j][l], poly[j][(l + 1) % m[j]])) {
                        adj[idx[i]][idx[j]] = adj[idx[j]][idx[i]] = true;
                    }
                }
            }
        }
    }

    vector<bool> ok(1 << N, true);
    for (int mask = 0; mask < (1 << N); mask++) {
        for (int i = 0; i < N; i++) {
            if (!(mask >> i & 1)) continue;
            for (int j = i + 1; j < N; j++) {
                if (!(mask >> j & 1)) continue;
                if (adj[i][j]) ok[mask] = false;
            }
        }
    }

    vector<int> dp(1 << N, N);
    dp[0] = 0;
    for (int mask = 0; mask < (1 << N); mask++) {
        for (int sub = mask; sub > 0; sub = (sub - 1) & mask) {
            if (ok[sub]) {
                dp[mask] = min(dp[mask], dp[mask ^ sub] + 1);
            }
        }
    }

    int ans = dp.back();
    cout << ans << '\n';
}

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    int n;
    while (cin >> n, n) solve(n);
    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3388
final_rank: 3

code_snippet: #define LOCAL
#include <bits/stdc++.h>
using namespace std;
#pragma region Macros
typedef long long ll;
typedef __int128_t i128;
typedef unsigned int uint;
typedef unsigned long long ull;
#define ALL(x) (x).begin(), (x).end()

template <typename T> istream& operator>>(istream& is, vector<T>& v) {
    for (T& x : v) is >> x;
    return is;
}
template <typename T> ostream& operator<<(ostream& os, const vector<T>& v) {
    for (int i = 0; i < (int)v.size(); i++) {
        os << v[i] << (i + 1 == (int)v.size() ? "" : " ");
    }
    return os;
}
template <typename T, typename U> ostream& operator<<(ostream& os, const pair<T, U>& p) {
    os << '(' << p.first << ',' << p.second << ')';
    return os;
}
template <typename T, typename U> ostream& operator<<(ostream& os, const map<T, U>& m) {
    os << '{';
    for (auto itr = m.begin(); itr != m.end();) {
        os << '(' << itr->first << ',' << itr->second << ')';
        if (++itr != m.end()) os << ',';
    }
    os << '}';
    return os;
}
template <typename T, typename U> ostream& operator<<(ostream& os, const unordered_map<T, U>& m) {
    os << '{';
    for (auto itr = m.begin(); itr != m.end();) {
        os << '(' << itr->first << ',' << itr->second << ')';
        if (++itr != m.end()) os << ',';
    }
    os << '}';
    return os;
}
template <typename T> ostream& operator<<(ostream& os, const set<T>& s) {
    os << '{';
    for (auto itr = s.begin(); itr != s.end();) {
        os << *itr;
        if (++itr != s.end()) os << ',';
    }
    os << '}';
    return os;
}
template <typename T> ostream& operator<<(ostream& os, const multiset<T>& s) {
    os << '{';
    for (auto itr = s.begin(); itr != s.end();) {
        os << *itr;
        if (++itr != s.end()) os << ',';
    }
    os << '}';
    return os;
}
template <typename T> ostream& operator<<(ostream& os, const unordered_set<T>& s) {
    os << '{';
    for (auto itr = s.begin(); itr != s.end();) {
        os << *itr;
        if (++itr != s.end()) os << ',';
    }
    os << '}';
    return os;
}
template <typename T> ostream& operator<<(ostream& os, const deque<T>& v) {
    for (int i = 0; i < (int)v.size(); i++) {
        os << v[i] << (i + 1 == (int)v.size() ? "" : " ");
    }
    return os;
}

template <int i, typename T> void print_tuple(ostream&, const T&) {}
template <int i, typename T, typename H, class... Args> void print_tuple(ostream& os, const T& t) {
    if (i) os << ',';
    os << get<i>(t);
    print_tuple<i + 1, T, Args...>(os, t);
}
template <typename... Args> ostream& operator<<(ostream& os, const tuple<Args...>& t) {
    os << '{';
    print_tuple<0, tuple<Args...>, Args...>(os, t);
    return os << '}';
}

void debug_out() { cerr << '\n'; }
template <class Head, class... Tail> void debug_out(Head&& head, Tail&&... tail) {
    cerr << head;
    if (sizeof...(Tail) > 0) cerr << ", ";
    debug_out(move(tail)...);
}
#ifdef LOCAL
#define debug(...)                                                                   \
    cerr << " ";                                                                     \
    cerr << #__VA_ARGS__ << " :[" << __LINE__ << ":" << __FUNCTION__ << "]" << '\n'; \
    cerr << " ";                                                                     \
    debug_out(__VA_ARGS__)
#else
#define debug(...) 42
#endif

template <typename T> T gcd(T x, T y) { return y != 0 ? gcd(y, x % y) : x; }
template <typename T> T lcm(T x, T y) { return x / gcd(x, y) * y; }

int topbit(signed t) { return t == 0 ? -1 : 31 - __builtin_clz(t); }
int topbit(long long t) { return t == 0 ? -1 : 63 - __builtin_clzll(t); }
int botbit(signed a) { return a == 0 ? 32 : __builtin_ctz(a); }
int botbit(long long a) { return a == 0 ? 64 : __builtin_ctzll(a); }
int popcount(signed t) { return __builtin_popcount(t); }
int popcount(long long t) { return __builtin_popcountll(t); }
bool ispow2(int i) { return i && (i & -i) == i; }

template <class T> T ceil(T x, T y) {
    assert(y >= 1);
    return (x > 0 ? (x + y - 1) / y : x / y);
}
template <class T> T floor(T x, T y) {
    assert(y >= 1);
    return (x > 0 ? x / y : (x - y + 1) / y);
}

template <class T1, class T2> inline bool chmin(T1& a, T2 b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}
template <class T1, class T2> inline bool chmax(T1& a, T2 b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}
#pragma endregion

const int INF = 1e9;
const long long IINF = 1e18;
const int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
const char dir[4] = {'D', 'R', 'U', 'L'};
const long long MOD = 1000000007;
// const long long MOD = 998244353;

void solve(int n) {
    vector<int> idx(n), m(n);
    vector<vector<pair<int, int>>> poly(n);
    map<string, int> mp;
    for (int i = 0; i < n; i++) {
        string S;
        cin >> S;
        if (!mp.count(S)) mp[S] = mp.size();
        idx[i] = mp[S];
        int x, y;
        while (cin >> x, x >= 0) {
            cin >> y;
            poly[i].emplace_back(x, y);
        }
        m[i] = poly[i].size();
    }

    auto check = [&](pair<int, int> a, pair<int, int> b, pair<int, int> c, pair<int, int> d) {
        if (a.first > b.first) swap(a, b);
        if (c.first > d.first) swap(c, d);
        pair<int, int> diag1, diag2;
        {
            int dx = b.first - a.first, dy = b.second - a.second;
            if (dx == 0) dy = 1;
            int g = gcd(dx, dy);
            diag1 = {dx / g, dy / g};
        }
        {
            int dx = d.first - c.first, dy = d.second - c.second;
            if (dx == 0) dy = 1;
            int g = gcd(dx, dy);
            diag2 = {dx / g, dy / g};
        }
        if (diag1 != diag2) return false;
        if (diag1.first == 0) {
            if (a.first != c.first) return false;
            if (a.second > b.second) swap(a, b);
            if (c.second > d.second) swap(c, d);
            return max(a.second, c.second) < min(b.second, d.second);
        }
        double y1 = a.second - double(a.first) / double(diag1.first) * diag1.second,
               y2 = c.second - double(c.first) / double(diag2.first) * diag2.second;
        if (y1 != y2) return false;
        return max(a.first, c.first) < min(b.first, d.first);
    };

    int N = mp.size();
    vector<vector<bool>> adj(N, vector<bool>(N, false));
    for (int i = 0; i < n; i++) {
        for (int j = i + 1; j < n; j++) {
            for (int k = 0; k < m[i]; k++) {
                for (int l = 0; l < m[j]; l++) {
                    if (check(poly[i][k], poly[i][(k + 1) % m[i]], poly[j][l], poly[j][(l + 1) % m[j]])) {
                        adj[idx[i]][idx[j]] = adj[idx[j]][idx[i]] = true;
                    }
                }
            }
        }
    }

    vector<bool> ok(1 << N, true);
    for (int mask = 0; mask < (1 << N); mask++) {
        for (int i = 0; i < N; i++) {
            if (!(mask >> i & 1)) continue;
            for (int j = i + 1; j < N; j++) {
                if (!(mask >> j & 1)) continue;
                if (adj[i][j]) ok[mask] = false;
            }
        }
    }

    vector<int> dp(1 << N, N);
    dp[0] = 0;
    for (int mask = 0; mask < (1 << N); mask++) {
        for (int sub = mask; sub > 0; sub = (sub - 1) & mask) {
            if (ok[sub]) {
                dp[mask] = min(dp[mask], dp[mask ^ sub] + 1);
            }
        }
    }

    int ans = dp.back();
    cout << ans << '\n';
}

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    int n;
    while (cin >> n, n) solve(n);
    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3384
final_rank: 2

code_snippet: #include<bits/stdc++.h>
using namespace std;
#define ALL(x) begin(x),end(x)
#define rep(i,n) for(int i=0;i<(n);i++)
#define debug(v) cout<<#v<<":";for(auto x:v){cout<<x<<' ';}cout<<endl;
#define mod 1000000007
using ll=long long;
const int INF=1000000000;
const ll LINF=1001002003004005006ll;
int dx[]={1,0,-1,0},dy[]={0,1,0,-1};
// ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
template<class T>bool chmax(T &a,const T &b){if(a<b){a=b;return true;}return false;}
template<class T>bool chmin(T &a,const T &b){if(b<a){a=b;return true;}return false;}

struct IOSetup{
    IOSetup(){
        cin.tie(0);
        ios::sync_with_stdio(0);
        cout<<fixed<<setprecision(12);
    }
} iosetup;

template<typename T>
ostream &operator<<(ostream &os,const vector<T>&v){
    for(int i=0;i<(int)v.size();i++) os<<v[i]<<(i+1==(int)v.size()?"":" ");
    return os;
}
template<typename T>
istream &operator>>(istream &is,vector<T>&v){
    for(T &x:v)is>>x;
    return is;
}

// graph template
// ref : https://ei1333.github.io/library/graph/graph-template.cpp
template<typename T=int>
struct Edge{
    int from,to;
    T w;
    int idx;
    Edge()=default;
    Edge(int from,int to,T w=1,int idx=-1):from(from),to(to),w(w),idx(idx){}
    operator int() const{return to;}
};

template<typename T=int>
struct Graph{
    vector<vector<Edge<T>>> g;
    int V,E;
    Graph()=default;
    Graph(int n):g(n),V(n),E(0){}

    int size(){
        return (int)g.size();
    }
    void resize(int k){
        g.resize(k);
        V=k;
    }
    inline const vector<Edge<T>> &operator[](int k)const{
        return (g.at(k));
    }
    inline vector<Edge<T>> &operator[](int k){
        return (g.at(k));
    }
    void add_directed_edge(int from,int to,T cost=1){
        g[from].emplace_back(from,to,cost,E++);
    }
    void add_edge(int from,int to,T cost=1){
        g[from].emplace_back(from,to,cost,E);
        g[to].emplace_back(to,from,cost,E++);
    }
    void read(int m,int pad=-1,bool weighted=false,bool directed=false){
        for(int i=0;i<m;i++){
            int u,v;cin>>u>>v;
            u+=pad,v+=pad;
            T w=T(1);
            if(weighted) cin>>w;
            if(directed) add_directed_edge(u,v,w);
            else         add_edge(u,v,w);
        }
    }
};

template<typename T>
int chromatic_number(Graph<T> G){
    int N=G.size();
    assert(N<28);

    vector<int> E(N,0);
    for(int i=0;i<N;i++){
        E[i]|=(1<<i);
        for(auto &e:G[i]) E[i]|=(1<<e.to);
    }

    // I[S] := Sの部分集合にある独立集合の個数
    vector<int> I(1<<N,0);
    I[0]=1;
    for(int S=1;S<(1<<N);S++){
        int v=__builtin_ctz(S);// 代表点
        I[S]=I[S&~(1<<v)]+I[S&~E[v]];
    }

    auto Pow=[](ll a,int k,ll p){
        ll ret=1;
        for(;k;k>>=1){
            if(k&1) ret=ret*a%p;
            a=a*a%p;
        }
        return ret;
    };

    for(int k=1;k<=N;k++){
        // g[S] := Sをちょうどカバーするk個の独立集合の組の個数
        // f[S] := Sの部分集合からk個重複を許して選ぶ個数
        //  -> F[S] = I[S]^k {(a or b .. ) * (a or b ...)...}
        //  -> f[S] = sum_{T in S} g[T]
        //  -> g[S] = sum_{T in S} (-1)^(|S|-|T|) f[T]
        for(ll p:{1000000007,1000000021}){
            ll g=0;
            for(int S=0;S<(1<<N);S++){
                if((N-__builtin_popcount(S))&1) g-=Pow(I[S],k,p);
                else                            g+=Pow(I[S],k,p);
                g=(g+p)%p;
            }
            if(g>0) return k;
        }
    }
    return 0;
}

int N;

using P=pair<int,int>;

void solve(){
    vector<vector<P>> poly(N);
    vector<int> belong(N);

    map<string,int> id;
    int n=0;
    rep(i,N){
        string S;cin>>S;
        if(!id.count(S)) id[S]=n++;
        belong[i]=id[S];

        int x,y;
        while(cin>>x,x>=0){
            cin>>y;
            poly[i].emplace_back(x,y);
        }
    }

    Graph<int> g(n);

    auto cross=[](P a,P b){
        return a.first*b.second-a.second*b.first;
    };

    auto dot=[&](P a,P b){
        return a.first*b.first+a.second*b.second;
    };

    auto norm=[&](P a){
        return a.first*a.first+a.second*a.second;
    };

    auto ison=[&](P a,P c,P p){
        c.first-=a.first,c.second-=a.second;
        p.first-=a.first,p.second-=a.second;
        if(c.first==0 and c.second==0) return false;
        if(p.first==0 and p.second==0) return false;
        if(p.first==c.first and p.second==c.second) return false;
        if(cross(p,c)>0) return false;
        if(cross(p,c)<0) return false;
        if(dot(p,c)<0) return false;
        return norm(p)<norm(c);
    };


    rep(i,N){
        rep(j,i){
            rep(ii,(int)poly[i].size()){
                rep(jj,(int)poly[j].size()){
                    P ipa=poly[i][ii],ipb=poly[i][(ii+1)%poly[i].size()];
                    P jpa=poly[j][jj],jpb=poly[j][(jj+1)%poly[j].size()];


                    if((ipb.second-ipa.second)*(jpb.first-jpa.first) == (jpb.second-jpa.second)*(ipb.first-ipa.first)){
                        bool f=false;
                        {
                            if(ipa==jpa and ipb==jpb) f=true;
                            if(ipa==jpb and ipb==jpa) f=true;
                        }
                        if(ison(ipa,ipb,jpa)) f=true;
                        if(ison(ipa,ipb,jpb)) f=true;
                        if(ison(jpa,jpb,ipa)) f=true;
                        if(ison(jpa,jpb,ipb)) f=true;
                        if(f){
                            // cout<<"cross"<<endl;
                            // cout<<belong[i]<<" - "<<belong[j]<<endl;
                            // cout<<"("<<ipa.first<<", "<<ipa.second<<") - ("<<ipb.first<<", "<<ipb.second<<")"<<endl;
                            // cout<<"("<<jpa.first<<", "<<jpa.second<<") - ("<<jpb.first<<", "<<jpb.second<<")"<<endl;
                            g.add_edge(belong[i],belong[j]);
                        }
                    }
                }
            }
        }
    }
    cout<<chromatic_number(g)<<endl;
}

signed main(){
    while(cin>>N,N) solve();
    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3420
final_rank: 6

code_snippet: #include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for (int i = 0; i < (n); ++i)
#define Inf 1000000001

const double eps = 1e-10;

template <typename T>
struct vector_2d{
	T x,y,rad,dir;
	
	vector_2d(T a=0.0,T b=0.0){
		x = a;
		y = b;
		fix_rd();
	}
	
	void update_x(T a,T b){
		x = a*x + b;
		fix_rd();
	}
	
	void update_x(T a){
		update_x(0.0,a);
	}
	
	void update_y(T a,T b){
		y = a*y + b;
		fix_rd();
	}
	
	void update_y(T a){
		update_y(0.0,a);
	}
	
	void update_rad(T a,T b){
		rad = a*rad + b;
		fix_xy();
	}
	
	void update_rad(T a){
		update_rad(0.0,a);
	}
	
	void update_dir(T a,T b){
		dir = a*dir + b;
		fix_xy();
	}

	void update_dir(T a){
		update_dir(0.0,a);
	}
	
	void fix_xy(){
		x = rad * cos(dir);
		y = rad * sin(dir);
		fix_rd();
	}
	
	void fix_rd(){
		rad = hypot(x,y);
		if(rad==0.0)dir=0.0;
		else dir = atan2(y,x);
		fix_zero();
	}
	
	void fix_zero(){
		if(abs(x)<eps)x = 0.0;
		if(abs(y)<eps)y = 0.0;
		if(abs(rad)<eps)rad = 0.0;
		if(abs(dir)<eps)dir = 0.0;
	}
	
	void normalize(){
		T s = size();
		update_x(1.0/s,0.0);
		update_y(1.0/s,0.0);
	}
	
	
	T get_dis(vector_2d<T> V){
		return hypot(x-V.x,y-V.y);
	}
	
	T size(){
		return get_dis(vector_2d<T>());
	}
	
	T angle_difference(vector_2d<T> V){
		double ret = dir - V.dir;
		if(ret<-acos(-1.0))ret = acos(-1.0)*2.0+ret;
		if(ret>acos(-1.0))ret=-acos(-1.0)*2.0+ret;
		return ret;
	}
	
	//中点
	vector_2d get_midpoint(vector_2d<T> V){
		V.update_x(0.5,x/2.0);
		V.update_y(0.5,y/2.0);
		return V;
	}
	
	T get_inner_product(vector_2d<T> V){
		return x*V.x+y*V.y;
	}
	
	T get_cross_product(vector_2d<T> V){
		return x*V.y-y*V.x;
	}
	
	vector_2d &operator+=(const vector_2d<T> &another){
		update_x(1,another.x);
		update_y(1,another.y);
		return (*this);
	}
	
	vector_2d &operator-=(const vector_2d<T> &another){
		update_x(1,-another.x);
		update_y(1,-another.y);
		return (*this);
	}
	
	vector_2d operator+(const vector_2d<T> &another)const{
		return (vector_2d(*this)+=another);
	}
	
	vector_2d operator-(const vector_2d<T> &another)const{
		return (vector_2d(*this)-=another);
	}
	
	void show(){
		cout<<x<<','<<y<<endl;
	}
};

//a+tx
template <typename T>
struct line{
	vector_2d<T> a,t;
	
	line(vector_2d<T> V1,vector_2d<T> V2){
		a=V1;
		t=V2-V1;
	}

	T get_signed_dis(vector_2d<T> V){
		vector_2d<double> PA = a-V;
		return PA.get_cross_product(t)/t.size();
	}
	
	T get_dis(vector_2d<T> V){
		return abs(get_signed_dis(V));
	}
	
	vector_2d<T> get_projection(vector_2d<T> P){
		T r = t.get_inner_product(P-a)/t.size();
		vector_2d<T> temp = t;
		temp.update_rad(0.0,r);
		return a+temp;
	}	
	
	vector_2d<T> get_cross_point(line<T> L){
		vector_2d<T> ret(1e20,1e20);
		if(abs(t.get_cross_product(L.t))<eps)return ret;

		T d0 = L.get_signed_dis(a);
		T d1 = L.get_signed_dis(a+t);
		vector_2d<T> temp = t;
		temp.x *= d0/(d1-d0);
		temp.y *= d0/(d1-d0);
		ret = a - temp;
		return ret;
	}
	
};

template <typename T>
struct segment{
	vector_2d<T> V1,V2;
	segment(vector_2d<T> a=vector_2d<T>(),vector_2d<T> b=vector_2d<T>()){
		V1=a;
		V2=b;
	}
	
	T get_dis(vector_2d<T> P){
		T ret = 1e20;
		line<T> L(V1,V2);
		vector_2d<T> Q = L.get_projection(P);
		if(Q.x+eps>min(V1.x,V2.x)&&Q.y+eps>min(V1.y,V2.y)
		&&Q.x<max(V1.x,V2.x)+eps&&Q.y<max(V1.y,V2.y)+eps)ret = min(ret,Q.get_dis(P));
		else{
			ret = min(ret,P.get_dis(V1));
			ret = min(ret,P.get_dis(V2));
		}
		return ret;
	}
	
	T get_dis(segment<T> l){
		if(get_cross_point(l).x<1e20)return 0.0;
		return min({get_dis(l.V1),get_dis(l.V2),l.get_dis(V1),l.get_dis(V2)});
	}
	
	vector_2d<T> get_cross_point(segment<T> l){
		line<T> L1(V1,V2),L2(l.V1,l.V2);
		vector_2d<T> P = L1.get_cross_point(L2);
		if(get_dis(P)<eps&&l.get_dis(P)<eps)return P;
		return vector_2d<T> (1e20,1e20);
	}
};

int ans;

void go(vector<vector<int>> &E,int cur,vector<int> colors){
	int m = 0;
	rep(i,colors.size())m = max(m,colors[i]);
	if(cur==E.size()){
		rep(i,E.size()){
			rep(j,E[i].size()){
				int to = E[i][j];
				if(colors[i]==colors[to])return;
			}
		}
		ans = min(ans,m+1);
		return;
	}
	rep(i,m+2){
		colors[cur] = i;
		go(E,cur+1,colors);
	}
	
}

int main(){
	
	int n;
	while(cin>>n){
		if(n==0)break;
		map<string,int> S;
		
		vector<int> id(n);
		vector<vector<int>> x(n),y(n);
		
		rep(i,n){
			string s;
			cin>>s;
			
			int t = S.size();
			if(S.count(s)){
				t = S[s];
			}
			S[s] = t;
			id[i] = t;
			
			while(true){
				int X;
				cin>>X;
				if(X==-1)break;
				int Y;
				cin>>Y;
				
				x[i].push_back(X);
				y[i].push_back(Y);
			}
			
		}
		//cout<<'a'<<endl;
		vector<vector<int>> E(S.size());
		
		rep(i,n){
			rep(j,n){
				if(id[i]<=id[j])continue;
				bool f = true;
				
				rep(k,x[i].size()){
					int t0 = k,t1 = (k+1)%x[i].size();
					vector_2d<double> v0(x[i][t0],y[i][t0]),v1(x[i][t1],y[i][t1]);
					segment<double> s0(v0,v1);
					rep(l,x[j].size()){
						int t2 = l,t3 = (l+1)%x[j].size();
						vector_2d<double> v2(x[j][t2],y[j][t2]),v3(x[j][t3],y[j][t3]);
						segment<double> s1(v2,v3);
						
						if(abs((v3-v2).get_cross_product(v1-v0))>eps)continue;
						bool f0 = (v2-v0).size()<eps;
						bool f1 = (v2-v1).size()<eps;
						bool f2 = (v3-v0).size()<eps;
						bool f3 = (v3-v1).size()<eps;
						if((f0&&f3) || (f1&&f2)){
							f = false;
							goto L;
						}
						if(!f0&&!f2&&s1.get_dis(v0)<eps){
							f = false;
							goto L;
						}
						if(!f1&&!f3&&s1.get_dis(v1)<eps){
							f = false;
							goto L;
						}
						if(!f0&&!f1&&s0.get_dis(v2)<eps){
							f = false;
							goto L;
						}
						if(!f2&&!f3&&s0.get_dis(v3)<eps){
							f = false;
							goto L;
						}
					}
				}
				L:;
				if(!f){
					E[id[i]].push_back(id[j]);
				}
				
			}
		}
		
		ans = 100000;
		vector<int> colors(S.size(),-1);
		go(E,0,colors);
		cout<<ans<<endl;
		
	}
	
	return 0;
}
accuracy: 1
time_ms: 470
memory_kb: 3948
final_rank: 19

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

//geometry library by maine

//basic
using Real = double;
using Point = complex<Real>;
#define x real()
#define y imag()
const Real EPS = 1e-8, PI = acos(-1), INF = 1e10;

istream& operator>>(istream& is, Point& p) {
    Real a, b;
    is >> a >> b;
    p = Point(a, b);
    return is;
}
ostream& operator<<(ostream& os, Point p) {
    return os << fixed << setprecision(10) << p.x << " " << p.y;
}

Point operator*(const Point& p, const Real& d) { return Point(p.x * d, p.y * d); }

bool eq(const Real& r, const Real& s) { return abs(r - s) < EPS; }
bool eq(const Point& p, const Point& q) { return abs(p - q) < EPS; }

namespace std {
bool operator<(const Point& a, const Point& b) {
    return !eq(a.x, b.x) ? a.x < b.x : a.y < b.y;
}
}  // namespace std

Real get_angle(const Point& a, const Point& b, const Point& c) {
    return arg((c - a) / (b - a));
}

inline int inc(int i, int N) {
    return (i == N - 1 ? 0 : i + 1);
}
inline int dec(int i, int N) {
    return (i == 0 ? N - 1 : i - 1);
}

//unit vector
Point e(const Point& p) { return p / abs(p); }

//angle transformation
Real radian_to_degree(Real r) { return r * 180.0 / PI; }
Real degree_to_radian(Real d) { return d * PI / 180.0; }

//basic functions
Real dot(const Point& p, const Point& q) { return p.x * q.x + p.y * q.y; }
Real cross(const Point& p, const Point& q) { return p.x * q.y - p.y * q.x; }
Point rotate(const Real& theta, const Point& p) {
    return {cos(theta) * p.x - sin(theta) * p.y, sin(theta) * p.x + cos(theta) * p.y};
}
Point rotate90(const Point& p) {
    return {-p.y, p.x};
}

//structs : Line, Segment, Circle
struct Line {
    Point a, b;
    Line() = default;
    Line(Point a, Point b) : a(a), b(b) {}
    Point d() const { return e(b - a); }  //direction vector
    Point n() const {
        Point dd = d();
        return {dd.y, -dd.x};
    }  //normal vector
};
struct Segment : Line {
    Segment() = default;
    Segment(Point a, Point b) : Line(a, b) {}
};
struct Circle {
    Point p;
    Real r;
    Circle() = default;
    Circle(Point p, Real r) : p(p), r(r){};
};
using Points = vector<Point>;
using Polygon = vector<Point>;
using Polygons = vector<Polygon>;
using Lines = vector<Line>;
using Segments = vector<Segment>;
using Circles = vector<Circle>;

//happy functions
//https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_C
int ccw(const Point& a, const Point& bb, const Point& cc) {
    Point b = bb - a, c = cc - a;
    if (cross(b, c) > EPS)
        return 1;  //COUNTER_CLOCKWISE
    if (cross(b, c) < -EPS)
        return -1;  //CLOCKWISE
    if (dot(b, c) < 0)
        return 2;  //ONLINE_BACK  c-a-b
    if (norm(b) < norm(c))
        return -2;  //ONLINE_FRONT a-b-c
    return 0;       //ON_SEGMENT   a-c-b
}

bool parallel(const Line& l, const Line& m) { return eq(cross(l.d(), m.d()), 0.0); }
bool orthogonal(const Line& l, const Line& m) { return eq(dot(l.d(), m.d()), 0.0); }
Point projection(const Line& l, const Point& p) { return l.a + (l.a - l.b) * (dot(p - l.a, l.a - l.b) / norm(l.a - l.b)); }
Point reflection(const Line& l, const Point& p) { return p + (projection(l, p) - p) * 2.0; }

//intersect
bool intersect(const Line& l, const Point& p) { return abs(ccw(l.a, l.b, p)) != 1; }
bool intersect(const Line& l, const Line& m) { return !parallel(l, m) || (parallel(l, m) && intersect(l, m.a)); }
bool intersect(const Segment& s, const Point& p) { return ccw(s.a, s.b, p) == 0; }
bool intersect(const Line& l, const Segment& s) { return cross(l.d(), s.a - l.a) * cross(l.d(), s.b - l.a) < EPS; }
bool intersect(const Segment& s, const Segment& t) {
    return ccw(s.a, s.b, t.a) * ccw(s.a, s.b, t.b) <= 0 && ccw(t.a, t.b, s.a) * ccw(t.a, t.b, s.b) <= 0;
}

//distance
Real distance(const Point& p, const Point& q) { return abs(p - q); }
Real distance(const Line& l, const Point& p) { return distance(p, projection(l, p)); }
Real distance(const Line& l, const Line& m) { return intersect(l, m) ? 0 : distance(l, m.a); }
Real distance(const Segment& s, const Point& p) {
    Point q = projection(s, p);
    return intersect(s, q) ? distance(p, q) : min(distance(s.a, p), distance(s.b, p));
}
Real distance(const Segment& s, const Segment& t) { return intersect(s, t) ? 0 : min({distance(s, t.a), distance(s, t.b), distance(t, s.a), distance(t, s.b)}); }
Real distance(const Line& l, const Segment& s) { return intersect(l, s) ? 0 : min(distance(l, s.a), distance(l, s.b)); }

//intersect circle
bool intersect(const Circle& c, const Point& p) { return eq(distance(c.p, p), c.r); }
bool intersect(const Circle& c, const Line& l) { return distance(l, c.p) <= c.r + EPS; }
int intersect(const Circle& c, const Segment& s) {
    if (!intersect(c, Line(s)))
        return 0;
    Real da = distance(c.p, s.a), db = distance(c.p, s.b);
    if (da < c.r + EPS && db < c.r + EPS)
        return 0;
    if (da > db)
        swap(da, db);
    if (da < c.r - EPS && db > c.r + EPS)
        return 1;
    if (intersect(s, projection(s, c.p)))
        return 2;
    return 0;
}
int intersect(Circle c1, Circle c2) {
    if (c1.r < c2.r)
        swap(c1, c2);
    Real d = distance(c1.p, c2.p);
    if (c1.r + c2.r < d)
        return 4;
    if (eq(c1.r + c2.r, d))
        return 3;
    if (c1.r - c2.r < d)
        return 2;
    if (eq(c1.r - c2.r, d))
        return 1;
    return 0;
}

//crosspoint
Point crosspoint(const Line& l, const Line& m) {
    Real A = cross(l.b - l.a, m.b - m.a);
    Real B = cross(l.b - l.a, l.b - m.a);
    if (eq(abs(A), 0.0) && eq(abs(B), 0.0))
        return m.a;
    return m.a + (m.b - m.a) * B / A;
}
Point crosspoint(const Segment& s, const Segment& t) { return crosspoint(Line(s), Line(t)); }
pair<Point, Point> crosspoint(const Circle& c, const Line& l) {
    Point p = projection(l, c.p);
    if (eq(distance(l, c.p), c.r))
        return {p, p};
    Real len = sqrt(c.r * c.r - norm(p - c.p));
    return {p - len * l.d(), p + len * l.d()};
}
pair<Point, Point> crosspoint(const Circle& c, const Segment& s) {
    Line l(s);
    if (intersect(c, s) == 2)
        return crosspoint(c, l);
    auto ret = crosspoint(c, l);
    if (dot(s.a - ret.first, s.b - ret.first) < 0)
        ret.second = ret.first;
    else
        ret.first = ret.second;
    return ret;
}
pair<Point, Point> crosspoint(const Circle& c1, const Circle& c2) {
    Real d = distance(c1.p, c2.p);
    Real a = acos((c1.r * c1.r + d * d - c2.r * c2.r) / (2 * c1.r * d));
    Real t = atan2(c2.p.y - c1.p.y, c2.p.x - c1.p.x);
    Point p1 = c1.p + Point(cos(t + a) * c1.r, sin(t + a) * c1.r);
    Point p2 = c1.p + Point(cos(t - a) * c1.r, sin(t - a) * c1.r);
    return {p1, p2};
}

//tangent
pair<Point, Point> tangent(const Circle& c, const Point& p) {
    return crosspoint(c, Circle(p, sqrt(norm(c.p - p) - c.r * c.r)));
}
Lines tangent(Circle c1, Circle c2) {
    Lines ret;
    if (c1.r < c2.r)
        swap(c1, c2);
    if (eq(c1.p, c2.p))
        return ret;
    Real g = norm(c1.p - c2.p);
    Point u = (c2.p - c1.p) / sqrt(g);
    Point v = rotate(PI / 2, u);
    for (int s : {-1, 1}) {
        Real h = (c1.r + s * c2.r) / sqrt(g);
        if (eq(1 - h * h, 0)) {
            ret.emplace_back(c1.p + u * c1.r, c1.p + (u + v) * c1.r);
        } else if (1 - h * h > 0) {
            Point uu = u * h, vv = v * sqrt(1 - h * h);
            ret.emplace_back(c1.p + (uu + vv) * c1.r, c2.p - (uu + vv) * c2.r * s);
            ret.emplace_back(c1.p + (uu - vv) * c1.r, c2.p - (uu - vv) * c2.r * s);
        }
    }
    return ret;
}

//convex
//https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/3/CGL_3_B
bool is_convex(const Polygon& P) {
    int N = (int)P.size();
    for (int i = 0; i < N; i++) {
        if (ccw(P[i], P[(i + 1) % N], P[(i + 2) % N]) == -1)
            return false;
    }
    return true;
}
//https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/4/CGL_4_A
Polygon convex_hull(Points P) {
    int N = (int)P.size(), k = 0;
    if (N <= 2)
        return P;
    sort(begin(P), end(P));
    Points ret(2 * N);
    for (int i = 0; i < N; ret[k++] = P[i++]) {
        while (k >= 2 && cross(ret[k - 1] - ret[k - 2], P[i] - ret[k - 1]) < EPS)
            --k;
    }
    for (int i = N - 2, t = k + 1; i >= 0; ret[k++] = P[i--]) {
        while (k >= t && cross(ret[k - 1] - ret[k - 2], P[i] - ret[k - 1]) < EPS)
            --k;
    }
    ret.resize(k - 1);
    return ret;
}
//https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/4/CGL_4_B
Real convex_diameter(const Polygon& P) {
    int N = (int)P.size();
    int is = 0, js = 0;
    for (int i = 1; i < N; i++) {
        if (P[i].y < P[is].y)
            is = i;
        if (P[i].y > P[js].y)
            js = i;
    }
    Real maxdist = norm(P[is] - P[js]);
    int maxi, maxj, i, j;
    maxi = i = is;
    maxj = j = js;
    do {
        if (cross(P[inc(i, N)] - P[i], P[inc(j, N)] - P[j]) >= 0)
            j = inc(j, N);
        else
            i = inc(i, N);
        if (norm(P[i] - P[j]) > maxdist) {
            maxdist = norm(P[i] - P[j]);
            maxi = i;
            maxj = j;
        }
    } while (i != is || j != js);
    maxi = maxi;
    maxj = maxj;
    return sqrt(maxdist);
}
//https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/4/CGL_4_C
Polygon convex_cut(const Polygon& P, const Line& l) {
    Polygon ret;
    int N = (int)P.size();
    for (int i = 0; i < N; i++) {
        Point now = P[i], nxt = P[(inc(i, N))];
        if (ccw(l.a, l.b, now) != -1)
            ret.emplace_back(now);
        if (ccw(l.a, l.b, now) * ccw(l.a, l.b, nxt) == -1)
            ret.emplace_back(crosspoint(Line(now, nxt), l));
    }
    return ret;
}

//other
enum
{
    OUT,
    ON,
    IN
};

//https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/3/CGL_3_C
int contains(const Polygon& Q, const Point& p) {
    bool in = false;
    int N = (int)Q.size();
    for (int i = 0; i < N; i++) {
        Point a = Q[i] - p, b = Q[(i + 1) % N] - p;
        if (a.y > b.y)
            swap(a, b);
        if (a.y <= 0 && 0 < b.y && cross(a, b) < 0)
            in = !in;
        if (cross(a, b) == 0 && dot(a, b) <= 0)
            return ON;
    }
    return in ? IN : OUT;
}

//https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/3/CGL_3_A
Real area(const Polygon& P) {
    Real A = 0;
    for (int i = 0; i < (int)P.size(); i++) {
        A += cross(P[i], P[(i + 1) % P.size()]);
    }
    return A * 0.5;
}

//https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_H
Real cross_area(const Circle& c, const Point& a, const Point& b) {
    Point va = c.p - a, vb = c.p - b;
    Real f = cross(va, vb), ret = 0.0;
    if (eq(f, 0.0))
        return ret;
    if (max(abs(va), abs(vb)) < c.r + EPS)
        return f;
    if (distance(Segment(a, b), c.p) > c.r - EPS)
        return c.r * c.r * arg(vb * conj(va));
    auto u = crosspoint(c, Segment(a, b));
    vector<Point> V{a, u.first, u.second, b};
    for (int i = 0; i < (int)V.size() - 1; i++) {
        ret += cross_area(c, V[i], V[i + 1]);
    }
    return ret;
}
Real area(const Polygon& p, const Circle& c) {
    int N = (int)p.size();
    if (N < 3)
        return 0.0;
    Real ret = 0;
    for (int i = 0; i < N; i++) {
        ret += cross_area(c, p[i], p[inc(i, N)]);
    }
    return ret / 2.0;
}

//https://onlinejudge.u-aizu.ac.jp/problems/CGL_5_A
Real closest_pair(Points& P, int left, int right) {
    if (right - left <= 1)
        return 1e18;
    int mid = (left + right) / 2;
    Real xx = P[mid].x;
    Real dist = min(closest_pair(P, left, mid), closest_pair(P, mid, right));
    inplace_merge(P.begin() + left, P.begin() + mid, P.begin() + right, [&](const Point& a, const Point& b) { return a.y < b.y; });
    Points memo;
    memo.reserve(right - left);
    for (int i = left; i < right; i++) {
        if (abs(P[i].x - xx) >= dist)
            continue;
        for (int j = (int)memo.size() - 1; j >= 0; j--) {
            if (P[i].y - memo[j].y >= dist)
                break;
            dist = min(dist, distance(P[i], memo[j]));
        }
        memo.emplace_back(P[i]);
    }
    return dist;
}
Real closest_pair(Points P) {
    sort(begin(P), end(P));
    return closest_pair(P, 0, (int)P.size());
}

//https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_B
Circle incircle(Polygon P) {
    assert((int)P.size() == 3);
    Real l0 = distance(P[1], P[2]), l1 = distance(P[0], P[2]), l2 = distance(P[0], P[1]);
    Circle c;
    c.p = crosspoint(Line(P[0], (P[1] * l1 + P[2] * l2) / (l1 + l2)), Line(P[1], (P[0] * l0 + P[2] * l2) / (l0 + l2)));
    c.r = distance(Line(P[0], P[1]), c.p);
    return c;
}

//https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_C
Circle circumcircle(Polygon P) {
    assert((int)P.size() == 3);
    Circle c;
    c.p = crosspoint(Line((P[0] + P[1]) / 2.0, (P[0] + P[1]) / 2.0 + rotate90(P[0] - P[1])),
        Line((P[0] + P[2]) / 2.0, (P[0] + P[2]) / 2.0 + rotate90(P[0] - P[2])));
    c.r = distance(c.p, P[0]);
    return c;
}

#undef x
#undef y

int chromatic(vector<vector<int>> G) {
    int N = (int)G.size();
    if (N == 0)
        return 0;
    vector<int> mask(N);
    for (int i = 0; i < N; i++) {
        for (auto&& j : G[i]) {
            mask[i] |= 1 << j;
        }
    }
    vector<int> dp(1 << N);
    for (int i = 0; i < (1 << N); i++) {
        dp[i] = ((N - __builtin_popcount(i)) & 1 ? -1 : 1);
    }
    auto calc = [&](const int mod) {
        vector<int> ret(1 << N);
        ret[0] = 1;
        for (int bit = 1; bit < (1 << N); bit++) {
            int ctz = __builtin_ctz(bit);
            ret[bit] = ret[bit ^ (1 << ctz)] + ret[(bit - (1 << ctz)) & ~mask[ctz]];
            if (ret[bit] >= mod)
                ret[bit] -= mod;
        }
        return ret;
    };
    const int mod1 = 1077563119, mod2 = 1000000007;
    auto cnt1 = calc(mod1);
    auto cnt2 = calc(mod2);
    using ll = long long;
    auto dp1 = dp, dp2 = dp;
    for (int i = 1; i < N; i++) {
        ll sum1 = 0, sum2 = 0;
        for (int bit = 0; bit < (1 << N); bit++) {
            dp1[bit] = (int)(((ll)dp1[bit] * cnt1[bit]) % mod1);
            sum1 += dp1[bit];
            dp2[bit] = (int)(((ll)dp2[bit] * cnt2[bit]) % mod2);
            sum2 += dp2[bit];
        }
        if (sum1 % mod1 != 0 || sum2 % mod2 != 0)
            return i;
    }
    return N;
}

int main() {
    int N;
    while (cin >> N, N) {
        map<string, Polygons> mp;
        while (N--) {
            string S;
            cin >> S;
            int x, y;
            Polygon P;
            while (cin >> x, x >= 0) {
                cin >> y;
                P.emplace_back(x, y);
            }
            mp[S].emplace_back(P);
        }
        N = 0;
        map<string, int> idx;
        for (auto&& [key, vec] : mp) {
            if (!idx.count(key))
                idx[key] = N++;
        }
        vector<vector<int>> G(N);
        for (auto&& [key1, vec1] : mp) {
            for (auto&& [key2, vec2] : mp) {
                int idx1 = idx[key1], idx2 = idx[key2];
                if (idx1 == idx2)
                    continue;
                bool flag = false;
                for (auto&& P1 : vec1) {
                    for (auto&& P2 : vec2) {
                        int N1 = (int)P1.size(), N2 = (int)P2.size();
                        for (int i1 = 0; i1 < N1; i1++) {
                            for (int i2 = 0; i2 < N2; i2++) {
                                auto s1 = Segment(P1[i1], P1[inc(i1, N1)]), s2 = Segment(P2[i2], P2[inc(i2, N2)]);
                                if (intersect(s1, s2) && parallel(s1, s2)) {
                                    if ((intersect(s1, Segment(s2.a, s2.b + s2.d() * 0.1)) && intersect(s1, Segment(s2.a, s2.b - s2.d() * 0.1)))
                                        && (intersect(s1, Segment(s2.b, s2.a + s2.d() * 0.1)) && intersect(s1, Segment(s2.b, s2.a - s2.d() * 0.1))))
                                        flag = true;
                                }
                            }
                        }
                    }
                }
                if (flag)
                    G[idx1].emplace_back(idx2);
            }
        }
        cout << chromatic(G) << endl;
    }
}
accuracy: 1
time_ms: 10
memory_kb: 3448
final_rank: 7

code_snippet: #pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef unsigned long long ull;

mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
ll myRand(ll B) {
    return (ull)rng() % B;
}

struct Point{
    int x,y;
};

int dot(Point a,Point b){
    return a.x*b.x+a.y*b.y;
}

int cross(Point a,Point b){
    return a.x*b.y-a.y*b.x;
}

int ccw(Point a,Point b,Point c){
    b={b.x-a.x,b.y-a.y};
    c={c.x-a.x,c.y-a.y};
    if(cross(b,c)>0)return 1; // a,b,c反時計回り
    if(cross(b,c)<0)return -1; // a,b,c時計周り
    if(dot(b,c)<0)return 2; // c,a,b 一直線
    if((b.x*b.x+b.y*b.y)<(c.x*c.x+c.y*c.y))return -2; // a,b,c 一直線
    return 0; // a,c,b 一直線
}

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    int n;
    while(cin >> n,n){
        int m=0;
        map<string,int> mp;
        vector<string> s(n);
        vector<vector<pair<int,int>>> v(n); 
        for(int i=0;i<n;i++){
            cin >> s[i];
            if(mp.find(s[i])==mp.end()){
                mp[s[i]]=m; m++;
            }
            int x,y;
            while(cin >> x,x!=-1){
                cin >> y;
                v[i].push_back({x,y});
            }
        }
        auto check=[&](int i,int j)->bool{
            for(int k=0;k<v[i].size();k++){
                Point A={v[i][k].first,v[i][k].second};
                Point B={v[i][(k+1)%v[i].size()].first,v[i][(k+1)%v[i].size()].second};
                for(int kk=0;kk<v[j].size();kk++){
                    Point C={v[j][kk].first,v[j][kk].second};
                    // if(A.x==C.x and A.y==C.y)continue;
                    // if(B.x==C.x and B.y==C.y)continue;
                    if(ccw(A,B,C)==0){
                        if(A.x==C.x and A.y==C.y){
                            Point D={v[j][(kk+1)%v[j].size()].first,v[j][(kk+1)%v[j].size()].second};
                            if(ccw(A,B,D)==0)return true;
                            D={v[j][(kk+v[j].size()-1)%v[j].size()].first,v[j][(kk+v[j].size()-1)%v[j].size()].second};
                            if(ccw(A,B,D)==0)return true;
                        }
                        else if(B.x==C.x and B.y==C.y){
                            Point D={v[j][(kk+1)%v[j].size()].first,v[j][(kk+1)%v[j].size()].second};
                            if(ccw(A,B,D)==0)return true;
                            D={v[j][(kk+v[j].size()-1)%v[j].size()].first,v[j][(kk+v[j].size()-1)%v[j].size()].second};
                            if(ccw(A,B,D)==0)return true;
                        }
                        else return true;
                    }
                }
            }
            return false;
        };
        vector<vector<int>> g(m);
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                if(s[i]==s[j])continue;
                if(check(i,j)){
                    // cout << i << " " << j << endl;
                    g[mp[s[i]]].push_back(mp[s[j]]);
                    g[mp[s[j]]].push_back(mp[s[i]]);
                }
            }
        }
        vector<int> dp(1<<m,1e9);
        dp[0]=0;
        for(int i=1;i<(1<<m);i++){
            for(int j=i;;j=(j-1)&i){
                int k=(j^i);
                bool ok=true;
                for(int u=0;u<m;u++){
                    if((1<<u)&k){
                        for(int t:g[u]){
                            if((1<<t)&k)ok=false;
                        }
                    }
                }
                if(ok){
                    dp[i]=min(dp[i],dp[j]+1);
                }
                if(j==0)break;
            }
        }
        printf("%d\n",dp.back());
    }
}
accuracy: 1
time_ms: 40
memory_kb: 3292
final_rank: 13

code_snippet: #include <bits/stdc++.h>
//#include <atcoder/all>
//using namespace atcoder;
#pragma GCC target ("avx")
#pragma GCC optimization ("O3")
#pragma GCC optimization ("unroll-loops")

using namespace std;

typedef vector<int> VI;
typedef vector<VI> VVI;
typedef vector<string> VS;
typedef pair<int, int> PII;
typedef pair<int, int> pii;
typedef pair<long long, long long> PLL;
typedef pair<int, PII> TIII;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;


#define FOR(i, s, n) for (int i = s; i < (int)n; ++i)
#define REP(i, n) FOR(i, 0, n)
#define rep(i, a, b) for (int i = a; i < (b); ++i)
#define trav(a, x) for (auto &a : x)
#define all(x) x.begin(), x.end()

#define MOD 1000000007

template<class T1, class T2> inline bool chmax(T1 &a, T2 b) {if (a < b) {a = b; return true;} return false;}
template<class T1, class T2> inline bool chmin(T1 &a, T2 b) {if (a > b) {a = b; return true;} return false;}
const double EPS = 1e-6, PI = acos(-1);
const double pi = 3.141592653589793238462643383279;
//ここから編集    
typedef string::const_iterator State;
ll GCD(ll a, ll b){
  return (b==0)?a:GCD(b, a%b);
}
ll LCM(ll a, ll b){
  return a/GCD(a, b) * b;
}
template< int mod >
struct ModInt {
  int x;

  ModInt() : x(0) {}

  ModInt(int64_t y) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}

  ModInt &operator+=(const ModInt &p) {
    if((x += p.x) >= mod) x -= mod;
    return *this;
  }

  ModInt &operator-=(const ModInt &p) {
    if((x += mod - p.x) >= mod) x -= mod;
    return *this;
  }

  ModInt &operator*=(const ModInt &p) {
    x = (int) (1LL * x * p.x % mod);
    return *this;
  }

  ModInt &operator/=(const ModInt &p) {
    *this *= p.inverse();
    return *this;
  }

  ModInt operator-() const { return ModInt(-x); }

  ModInt operator+(const ModInt &p) const { return ModInt(*this) += p; }

  ModInt operator-(const ModInt &p) const { return ModInt(*this) -= p; }

  ModInt operator*(const ModInt &p) const { return ModInt(*this) *= p; }

  ModInt operator/(const ModInt &p) const { return ModInt(*this) /= p; }

  bool operator==(const ModInt &p) const { return x == p.x; }

  bool operator!=(const ModInt &p) const { return x != p.x; }

  ModInt inverse() const {
    int a = x, b = mod, u = 1, v = 0, t;
    while(b > 0) {
      t = a / b;
      swap(a -= t * b, b);
      swap(u -= t * v, v);
    }
    return ModInt(u);
  }

  ModInt pow(int64_t n) const {
    ModInt ret(1), mul(x);
    while(n > 0) {
      if(n & 1) ret *= mul;
      mul *= mul;
      n >>= 1;
    }
    return ret;
  }

  friend ostream &operator<<(ostream &os, const ModInt &p) {
    return os << p.x;
  }

  friend istream &operator>>(istream &is, ModInt &a) {
    int64_t t;
    is >> t;
    a = ModInt< mod >(t);
    return (is);
  }

  static int get_mod() { return mod; }
};

using modint = ModInt< 1000000007 >;
template< typename T >
struct Combination {
  vector< T > _fact, _rfact, _inv;

  Combination(int sz) : _fact(sz + 1), _rfact(sz + 1), _inv(sz + 1) {
    _fact[0] = _rfact[sz] = _inv[0] = 1;
    for(int i = 1; i <= sz; i++) _fact[i] = _fact[i - 1] * i;
    _rfact[sz] /= _fact[sz];
    for(int i = sz - 1; i >= 0; i--) _rfact[i] = _rfact[i + 1] * (i + 1);
    for(int i = 1; i <= sz; i++) _inv[i] = _rfact[i] * _fact[i - 1];
  }

  inline T fact(int k) const { return _fact[k]; }

  inline T rfact(int k) const { return _rfact[k]; }

  inline T inv(int k) const { return _inv[k]; }

  T P(int n, int r) const {
    if(r < 0 || n < r) return 0;
    return fact(n) * rfact(n - r);
  }

  T C(int p, int q) const {
    if(q < 0 || p < q) return 0;
    return fact(p) * rfact(q) * rfact(p - q);
  }

  T H(int n, int r) const {
    if(n < 0 || r < 0) return (0);
    return r == 0 ? 1 : C(n + r - 1, r);
  }
};

int modpow(ll x, ll n, int mod) {
  ll res = 1;
  while(n) {
    if(n&1) res = res*x % mod;
    x = x*x%mod;
    n >>= 1;
  }
  return res;
}
using Point = complex< double >;
bool eq(double a, double b){ return fabs(a-b) < EPS; }
namespace std {
  bool operator<(const Point a, const Point b) {
    if(eq(a.real(),b.real())) return a.imag() < b.imag();
    return a.real() < b.real();
  }
}

istream &operator>> (istream &is, Point &p) {
  double a, b;
  is >> a >> b;
  p = Point(a, b);
  return is;
}

ostream &operator<< (ostream &os, Point &p) {
  return os << fixed << setprecision(10) << p.real() << " " << p.imag();
}

bool operator<(const Point &a, const Point &b) {
  return a.real() != b.real() ? a.real() < b.real() : a.imag() < b.imag();
}

// rotate Φ(rad)
// x = r * cos(θ + Φ)
//   = r * cos(θ) * cos(Φ) - r * sin(θ) * sin(Φ)
//   = x * cos(Φ) - y * sin(Φ) (∵ cos(θ) = x/r, sin(θ) = y/r)  
Point rotate(double phi, const Point &p) {
  double x = p.real(), y = p.imag();
  return Point(x * cos(phi) - y * sin(phi), x * sin(phi) + y * cos(phi));
}

double radian_to_degree(double r) {
  return (r * 180.0 / PI);
}

double degree_to_radian(double d) {
  return (d * PI / 180.0);
}

struct Line{
  Point a, b;

  Line() = default;

  Line(Point a, Point b) : a(a), b(b){}

  Line(double A, double B, double C){
    //ax + by = c
    if(eq(A, 0)){
      a = Point(0, C/B), b = Point(1, C/B);
    }else if(eq(B, 0)){
      a = Point(C/A, 0), b = Point(C/A, 1);
    }else{
      a = Point(0, C/B), b = Point(C/A, 0);
    }
  }

  friend istream &operator>>(istream &is, Line &a) {
    return is >> a.a >> a.b;
  }
  friend ostream &operator<<(ostream &os, Line &a) {
    return os << a.a << " to " << a.b;
  }
};

struct Segment: Line {
  Segment() = default;

  Segment(Point a, Point b) : Line(a, b) {}
};

struct Circle {
  Point p;
  double r;

  Circle() = default;

  Circle(Point p, double r): p(p), r(r){}
};

using Points = vector<Point>;
using Polygon = vector<Point>;
using Segments = vector<Segment>;
using Lines = vector<Line>;
using Circles = vector<Circle>;

double cross(const Point &a, const Point &b) {
  return a.real() * b.imag() - a.imag() * b.real();
}

double dot(const Point& a, const Point &b) {
  return a.real() * b.real() + a.imag() * b.imag();
}

//https://mathtrain.jp/projection
Point projection(const Line &l, const Point &p){
  double t = dot(p - l.a, l.a-l.b) / norm(l.a - l.b);
  return l.a + (l.a - l.b) * t;
}

Point projection(const Segment &l, const Point &p){
  double t = dot(p - l.a, l.b-l.a) / norm(l.a - l.b);
  return l.a + (l.b - l.a) * t;
}

Point reflection(const Line &l, const Point &p){
  return p + (projection(l, p) - p) * 2.0;
}

int ccw(const Point &a, const Point &b, const Point &c) {
  if(cross(b-a, c-a) > EPS) return 1;   // "COUNTER_CLOCKWISE"
  if(cross(b-a, c-a) < -EPS) return -1; // "CLOCKWISE"
  if(dot(b-a, c-a) < -EPS) return 2;    // "ONLINE_BACK" c-a-b
  if(norm(b-a) < norm(c-a) - EPS) return -2;  // "ONLINE_FRONT" a-b-c
  return 0;                         // "ON_SEGMENT" a-c-b
}

bool parallel(const Line &a, const Line &b){
  return eq(cross(a.a-a.b, b.a-b.b), 0.0);
}
bool orthogonal(const Line &a, const Line &b){
  return eq(dot(a.a-a.b, b.a-b.b), 0.0);
}
enum { OUT, ON, IN };

int contains(const Polygon& Q, const Point& p){
  bool in = false;
  for(int i=0; i<Q.size(); i++){
    Point a = Q[i] - p, b = Q[(i+1)%Q.size()]-p;
    if(a.imag() > b.imag()) swap(a, b);
    if(a.imag() <= 0 && 0 < b.imag() && cross(a, b) < 0) in = !in;
    if(cross(a, b) == 0 && dot(a, b) <= 0) return ON;
  }
  return in ? IN : OUT;
}
bool intersect(const Segment &s, const Point &p){
  return ccw(s.a, s.b, p) == 0;
}

//直線の交差判定
bool intersect(const Line &a, const Line &b) {
  if(parallel(a, b)) return 0;
  return 1;
}

//線分が重なる/端点で交わるときtrue
bool intersect(const Segment &a, const Segment &b) {
  return ccw(a.a, a.b, b.a) * ccw(a.a, a.b, b.b) <= 0 && ccw(b.a, b.b, a.a) * ccw(b.a, b.b, a.b) <= 0;
}

bool intersect(const Line &l, const Segment &s) {
  
}

Point crosspoint(const Line &a, const Line &b){
  double d = cross(b.b-b.a, a.b-a.a);
  if(eq(d, 0.0)) return Point(1e9, 1e9); 
  
  return a.a + (a.b - a.a) * cross(b.b-b.a, b.b-a.a) / d;
}

Point crosspoint(const Segment &a, const Segment &b){
  return crosspoint(Line(a.a, a.b), Line(b.a, b.b));
}
double distance(const Point &a, const Point &b){
  return abs(a - b);
}
double distance(const Line &l, const Point &p){
  return abs( cross(p - l.a, l.b-l.a) / abs(l.b-l.a) );
}
double distance(const Segment &s, const Point &p){
  Point r = projection(s, p);
  if(intersect(s, r)) return abs(r-p);
  return min(abs(s.a - p), abs(s.b - p));
}
// double distance(const Line &a, const Line &b){

// }
// double distance(const Line &a, const Segment &b){

// }
double distance(const Segment &a, const Segment &b) {
  return intersect(a, b) ? 0 : min({distance(a, b.a), distance(a, b.b), distance(b, a.a), distance(b, a.b)});
}

/* 2円の交点 */
vector<Point> crosspointCC(Circle C1, Circle C2){
  vector<Point> ps;
  Point ab = C2.p - C1.p;
  double d = abs(ab);
  double rc = (C1.r * C1.r + d * d - C2.r * C2.r) / (2 * d);
  if(eq(d, 0) || C1.r < abs(rc)) return ps;

  double rs = sqrt(C1.r * C1.r - rc*rc);

  Point abN = ab * Point(0, rs/d);
  Point cp = C1.p + rc / d * ab;
  ps.push_back(cp + abN);
  if(!eq(norm(abN), 0))ps.push_back(cp-abN);
  return ps;
}

vector<Point> crosspointCL(Circle C, Line l){
  Point p = projection(l, C.p);

  Point e = (l.b-l.a)/abs(l.b-l.a);
  if(eq(distance(l, C.p), C.r)) {
    return vector<Point>{p, p};
  }
  double base = sqrt(C.r*C.r-norm(p-C.p));
  
  return vector<Point>{p+e*base, p-e*base};
}

/* 円同士の共通部分の面積を求める */
// verify: http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_I date: 2020/10/11
long double AreaofCC(Circle& C1, Circle& C2){
  if(C1.r > C2.r) swap(C1, C2);
  long double nor = norm(C1.p - C2.p);
  long double dist = sqrtl(nor);
  
  if(C1.r + C2.r < dist+EPS) return 0;

  if(dist + C1.r < C2.r + EPS) return C1.r * C1.r * PI;

  long double theta1 = acosl((nor + C1.r*C1.r - C2.r * C2.r)/(2*C1.r*dist));

  long double theta2 = acosl((nor + C2.r*C2.r - C1.r * C1.r)/(2*C2.r*dist));
  
  return (theta1 - sinl(theta1+ theta1) *0.5) * C1.r * C1.r + (theta2 - sinl(theta2+theta2) *0.5) * C2.r * C2.r;
}


Polygon convex_hull(Polygon &p)
{
    int n = (int)p.size(), k = 0;
    if (n <= 2)
        return p;
    sort(p.begin(), p.end());
    vector<Point> ch(n * 2);

    for (int i = 0; i < n; ch[k++] = p[i++])
    {
        while (k >= 2 && cross(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1]) < 0)
            --k;
    }

    for (int i = n - 2, t = k + 1; i >= 0; ch[k++] = p[i--])
    {
        while (k >= t && cross(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1]) < 0)
            --k;
    }
    ch.resize(k - 1);
    return ch;
}
double convex_diameter(const Polygon &p)
{
    int n = (int)p.size();
    if (n == 2)
        return abs(p[0] - p[1]);

    int is = 0, js = 0;
    for (int i = 1; i < n; ++i)
    {
        if (imag(p[i]) > imag(p[is]))
            is = i;
        if (imag(p[i]) < imag(p[js]))
            js = i;
    }

    double res = abs(p[is] - p[js]);
    int i, maxi, j, maxj;
    i = maxi = is;
    j = maxj = js;
    do
    {
        if (cross(p[(i + 1) % n] - p[i], p[(j + 1) % n] - p[j]) >= 0)
            j = (j + 1) % n;
        else
            i = (i + 1) % n;
        res = max(res, abs(p[i] - p[j]));
    } while (i != is || j != js);
    return res;
}

Polygon convex_cut(const Polygon &p, const Line l)
{
    Polygon ret;
    for (int i = 0; i < p.size(); ++i)
    {
        Point now = p[i], nxt = p[(i + 1) % p.size()];
        if (ccw(l.a, l.b, now) != -1) //交点が線分l上にあるとき
            ret.push_back(now);
        if (ccw(l.a, l.b, now) * ccw(l.a, l.b, nxt) < 0)
        {
            ret.push_back(crosspoint(Line(now, nxt), l));
        }
    }
    return (ret);
}
double closestpair(Points &a, int l, int r){
  if(r-l<=1) return 1e20;
  int mid = (l+r)/2;

  double X = a[mid].real();
  double d = min(closestpair(a, l, mid), closestpair(a, mid, r));
  inplace_merge(a.begin()+l, a.begin()+mid, a.begin()+r, [](const Point& pa, const Point& pb){
    return pa.imag() < pb.imag();
  });

  Points b;
  for(int i=l; i<r; i++){
    if(abs(a[i].real()-X) >= d) continue;
    for(int j=b.size()-1; j>=0; j--){
      if(abs((a[i]-b[j]).imag()) >= d) break;
      d = min(d, abs(a[i]-b[j]));
    }
    b.push_back(a[i]);
  }
  return d;
}

/* 円の交差判定 */
/* verify: http://judge.u-aizu.ac.jp/onlinejudge/finder.jsp?course=CGL date:2020/10/11 */
int intersectionCC(Circle c1, Circle c2){
  if(c1.r > c2.r) swap(c1, c2);
  double d1 = abs(c1.p-c2.p); 
  double d2 = c1.r + c2.r;
  if(d1 > d2) return 4; /* 互いに交点を持たない */
  else if(d1 == d2) return 3; /* 外接する場合 */
  else{
    if(c2.r == c1.r + d1) return 1; /* 内接する場合 */
    else if(c2.r > c1.r + d1) return 0; /* 包含 */
    else return 2; /* 交点を2つ持つ */
  }
}

/* 点集合が反時計回りに与えられる場合のみ */
double Area(Points &g){
  double res = 0;
  int n = g.size();
  REP(i,n){
    res += cross(g[i], g[(i+1)%n]);
  }
  return res/2.0;
}

/* 内接円 */
/* verify: http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_B&lang=ja date: 2020/10/11 */
pair<Point, double> incircle_of_a_Triangle(Points &p){

  double a = abs(p[1]-p[2]), b = abs(p[2]-p[0]), c = abs(p[0]-p[1]);
  double s = (a+b+c) / 2.0;
  double S = sqrtl(s*(s-a)*(s-b)*(s-c));
  double r = S/s; 
  
  Point pp = (a*p[0]+b*p[1]+c*p[2])/(a+b+c);
  return make_pair(pp, r);
} 

/* 外接円 */
pair<Point, double> circumscribed_circle_of_a_triangle(Points &p){

  Point m1((p[0]+p[1])/2.0), m2((p[1]+p[2])/2.0);
  Point v((p[1]-p[0]).imag(), (p[0]-p[1]).real()), w((p[1]-p[2]).imag(), (p[2]-p[1]).real());

  Line l1(m1, Point(v+m1)), l2(m2, Point(w+m2));

  Point x = crosspoint(l1, l2);
  double r = abs(x-p[0]);
  return make_pair(x, r);
}

/* ある点pを通る円cの接線 */
Points tangent_to_a_circle(Point p, Circle C){
  Points ps;
  double d = abs(C.p-p);
  if(eq(d,0)){
    ps.push_back(p);
  }else if(d>EPS){
    
    double d2 = sqrt(d*d-C.r*C.r);
    
    long double theta = acosl(d2/d);
    //cout << theta << endl;
    Point pp = C.p - p;
    Point p2 = rotate(-theta, pp);

    Point e = p2/abs(p2);
    Point ppp = e*d2;
    ps.push_back(p + ppp);
    p2 = rotate(theta, pp);
    e = p2/abs(p2);
    ppp = e*d2;
    ps.push_back(p + ppp);
    
  }
  return ps;
}

Lines tangent(Circle C1, Circle C2){
  Lines ls;
  if(C1.r < C2.r) swap(C1, C2);
  double d = norm(C1.p - C2.p);
  if(eq(d, 0)) return ls;
  Point u = (C2.p - C1.p) / sqrt(d);
  Point v = rotate(pi/2, u);

  for(double s: {-1, 1}) {
    double h = (C1.r + s * C2.r) / sqrt(d);
    if(eq(1-h*h, 0)) {
      ls.emplace_back(C1.p + u * C1.r, C1.p + (u+v) * C1.r);
    }else if(1-h*h>0){
      Point uu = u*h, vv = v*sqrt(1-h*h);
      ls.emplace_back(C1.p + (uu+vv) * C1.r, C2.p - (uu+vv) * C2.r * s);
      ls.emplace_back(C1.p + (uu-vv) * C1.r, C2.p - (uu-vv) * C2.r * s);
    }
  }
  return ls;
}
Line bisector(Point a, Point b){
    Point A = (a+b)*Point(0.5, 0); 
    return Line(A, A+(b-a)*Point(0, PI/2));
}

Polygon voronoi_cell(Polygon Q, Points P, int s){
    REP(i,P.size()){
        if(i!=s){
            Q = convex_cut(Q, bisector(P[s], P[i]));
        }
    }
    return Q;
}

bool check(Segment &s,Segment &t) {

  if(s.a != t.a && s.a != t.b && ccw(t.a, t.b, s.a) == 0) return true;
  if(s.b != t.a && s.b != t.b && ccw(t.a, t.b, s.b) == 0) return true;
  if(t.a != s.a && t.a != s.b && ccw(s.a, s.b, t.a) == 0) return true;
  if(t.b != s.a && t.b != s.b && ccw(s.a, s.b, t.b) == 0) return true;
  if(s.a == t.a && s.b == t.b) return true;
  if(s.a == t.b && s.b == t.a) return true;
  return false;
}
unsigned long xor128() {
        static unsigned long x = 123456789, y = 362436069, z = 521288629, w = 88675123;
        unsigned long t = (x ^ (x << 11));
        x = y; y = z; z = w;
        return (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)));
}
int RandomPrime() {
        auto prime = [&](int n) {
                for (int i = 2; i * i <= n; i ++) if (n % i == 0) return false;
                return true;
        };
        int modulo = 1000000000;
        modulo += (int) (xor128() % 100000000);
        while (!prime(modulo)) modulo ++;
        return modulo;
}
int ChromaticNumber(const vector<vector<int>> &tmpg) {
        int n = tmpg.size();
        if (n == 0) return 0;
        vector<int> g(n);
        for (int i = 0; i < n; i ++) {
                for (int j = 0; j < n; j ++) {
                        if (tmpg[i][j]) {
                                g[i] |= (1 << j);
                        }
                }
        }
        int all = 1 << n;
        vector<int> a(all), s(all);
        a[0] = 1;
        int modulo = RandomPrime();
        for (int i = 1; i < all; i ++) {
                int j = __builtin_ctz(i);
                a[i] = a[i - (1 << j)] + a[(i - (1 << j)) & ~g[j]];
                if (a[i] >= modulo) a[i] -= modulo;
        }
        for (int i = 0; i < all; i ++) {
                s[i] = ((n - __builtin_popcount(i)) & 1 ? -1 : 1);
        }
        for (int k = 1; k < n; k ++) {
                long long sum = 0;
                for (int i = 0; i < all; i ++) {
                        long long cur = ((s[i] * (long long) a[i]) % modulo);
                        s[i] = (int) cur;
                        sum += cur;
                }
                if (sum % modulo != 0) return k;
        }
        return n;
}
int main() {
  cin.tie(0);
  ios::sync_with_stdio(false);
  cout << fixed << setprecision(7);
  
  int n;
  while(cin >> n, n) {
    map<string, vector<Polygon>> mp;
    
    REP(i,n) {
      Polygon ps;
      string name;
      cin >> name;
      int x;
      while(cin >> x) {
        if(x == -1) break;
        int y; cin >> y;
        Point p(x, y);
        ps.push_back(p);
      }
      mp[name].push_back(ps);
    }
    int n = mp.size();
    vector<vector<int>> g(n, vector<int>(n));
    
    int pt1=0, pt2=0;
    for(auto obj1 : mp) {
      pt2 = 0;
      for(auto obj2: mp) {
        if(pt1 == pt2) {
          pt2++;
          continue;
        }
        bool is_adj = false;

        for(int i=0; i<obj1.second.size(); i++){

          for(int j=0; j<obj2.second.size(); j++) {

            Polygon p1 = obj1.second[i];
            Polygon p2 = obj2.second[j];

            for(int k=0; k<p1.size(); k++) {
              for(int l=0; l<p2.size(); l++) {
                Segment s1(p1[k], p1[(k+1)%p1.size()]);
                Segment s2(p2[l], p2[(l+1)%p2.size()]);
                
                is_adj |= check(s1, s2);
              }
            }
          }
        }
        if(is_adj) {
          g[pt1][pt2] = true;
        }
        pt2++;
      }
      pt1++;
    }
  
    cout << ChromaticNumber(g) << endl;
  }

  return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 3232
final_rank: 12

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

using ll = long long;
using PII = pair<ll, ll>;

#define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i)
#define REP(i, n) FOR(i, 0, n)
#define ALL(x) x.begin(), x.end()
template <typename T> void chmin(T &a, const T &b) { a = min(a, b); }
template <typename T> void chmax(T &a, const T &b) { a = max(a, b); }

const ll INF = 1LL << 60;

struct FastIO {
    FastIO() {
        cin.tie(0);
        ios::sync_with_stdio(0);
    }
} fastiofastio;

namespace Geometry {
using R = long double; // Rにmint渡せるようにする
using P = complex<R>;
using L = pair<P, P>;
using G = vector<P>;
struct C {
    P c;
    R r;
    C() {}
    C(const P &a, const R &b) : c(a), r(b) {}
};
struct S : public L {
    S() {}
    S(const P &a, const P &b) : L(a, b) {}
};

const R EPS = 1e-8;
const R PI = atan(1) * 4;

inline int sgn(const R &r) { return (r > EPS) - (r < -EPS); }
inline R dot(const P &a, const P &b) {
    return real(a) * real(b) + imag(a) * imag(b);
}
inline R det(const P &a, const P &b) {
    return real(a) * imag(b) - imag(a) * real(b);
}
inline P rotate(const P &p, const R &arg) {
    return P(cos(arg) * p.real() - sin(arg) * p.imag(),
             sin(arg) * p.real() + cos(arg) * p.imag());
}
inline P vec(const L &l) { return l.second - l.first; }

// P,L,Sについて入力
inline istream &operator>>(istream &is, P &p) {
    R x, y;
    is >> x >> y;
    p = P(x, y);
    return is;
}
inline istream &operator>>(istream &is, L &l) {
    P a, b;
    is >> a >> b;
    l = L(a, b);
    return is;
}
inline istream &operator>>(istream &is, S &s) {
    P a, b;
    is >> a >> b;
    s = S(a, b);
    return is;
}

// 線分abから見たcの位置
enum CCW { LEFT = 1, RIGHT = 2, BACK = 4, FRONT = 8, ON_SEG = 16 };
int ccw(P a, P b, P c) {
    P p = (c - a) / (b - a);
    if (sgn(imag(p)) > 0)
        return LEFT;
    if (sgn(imag(p)) < 0)
        return RIGHT;
    if (sgn(real(p)) < 0)
        return BACK;
    if (sgn(real(p) - 1) > 0)
        return FRONT;
    return ON_SEG;
}

// 射影
P inline projection(const L &l, const P &p) {
    R t = dot(p - l.first, l.first - l.second) / norm(l.first - l.second);
    return l.first + t * (l.first - l.second);
}
// 反射
P inline reflection(const L &l, const P &p) {
    return p + (R)2 * (projection(l, p) - p);
}

// 垂直,平行
inline bool vertical(L a, L b) { return sgn(dot(vec(a), vec(b))) == 0; }
inline bool parallel(L a, L b) { return sgn(det(vec(a), vec(b))) == 0; }

// 交差判定
template <bool strict = false> inline bool intersect(const L &l1, const L &l2) {
    if (strict)
        return sgn(det(vec(l1), vec(l2))) != 0;
    return sgn(det(vec(l1), vec(l2))) != 0 || l1 == l2;
}
template <bool strict = false> inline bool intersect(const L &l, const S &s) {
    if (strict)
        det(s.first, vec(l)) * det(s.second, vec(l)) < 0;
    return det(s.first, vec(l)) * det(s.second, vec(l)) <= 0;
}
template <bool strict = false> inline bool intersect(const S &s1, const S &s2) {
    int ccw1 = ccw(s1.first, s1.second, s2.first) |
               ccw(s1.first, s1.second, s2.second);
    int ccw2 = ccw(s2.first, s2.second, s1.first) |
               ccw(s2.first, s2.second, s1.second);
    if (strict)
        return (ccw1 & ccw2) == (LEFT | RIGHT);
    return (ccw1 & ccw2) == (LEFT | RIGHT) || ((ccw1 | ccw2) & ON_SEG);
}
template <bool strict = false> inline bool intersect(const S &s, const P &p) {
    return ccw(s.first, s.second, p) == ON_SEG;
}
template <bool strict = false> inline bool intersect(const L &l, const P &p) {
    return ccw(l.first, l.second, p) == ON_SEG ||
           ccw(l.first, l.second, p) == FRONT ||
           ccw(l.first, l.second, p) == BACK;
}

// 距離
R dist(const S &s, const P &p) {
    P q = projection(s, p);
    if (sgn(dot(s.second - s.first, p - s.first)) <= 0)
        q = s.first;
    if (sgn(dot(s.first - s.second, p - s.second)) <= 0)
        q = s.second;
    return abs(p - q);
}
R dist(const S &a, const S &b) {
    if (intersect(a, b))
        return 0;
    return min({dist(a, b.first), dist(a, b.second), dist(b, a.first),
                dist(b, a.second)});
}
R dist(const L &l, const P &p) {
    P q = projection(l, p);
    return abs(p - q);
}

// 交点 交差判定を先にすること!!!
inline P crosspoint(const L &l1, const L &l2) {
    R ratio = det(vec(l2), l2.first - l1.first) / det(vec(l2), vec(l1));
    return l1.first + vec(l1) * ratio;
}

// 凸包 3点が一直線上に並ぶときに注意
// 凸包のうち一番左にある頂点の中で一番下の頂点から時計回り
G convex_hull(G ps) {
    int n = ps.size(), k = 0;
    sort(ps.begin(), ps.end());
    G r(2 * n);
    for (int i = 0; i < n; i++) {
        while (k > 1 && sgn(det(r[k - 1] - r[k - 2], ps[i] - r[k - 2])) < 0)
            k--;
        r[k++] = ps[i];
    }
    for (int i = n - 2, t = k; i >= 0; i--) {
        while (k > t && sgn(det(r[k - 1] - r[k - 2], ps[i] - r[k - 2])) < 0)
            k--;
        r[k++] = ps[i];
    }
    r.resize(k - 1);
    return r;
}

// 凸多角形polを直線lで切断して左側の多角形を返す
G convex_cut(const G &pol, const L &l) {
    G res;
    REP(i, pol.size()) {
        P a = pol[i], b = pol[(i + 1) % pol.size()];
        int da = sgn(det(l.first - a, l.second - a)),
            db = sgn(det(l.first - b, l.second - b));
        // 点aが直線lの左側
        if (da >= 0)
            res.emplace_back(a);
        // 辺(a,b)と直線lが交わる
        if (da * db < 0)
            res.emplace_back(crosspoint(L{a, b}, l));
    }
    return res;
}

// 1直線上に3点が並んでるような部分を消去 O(p.size())
G normalize_poligon(G p) {
    int n = p.size();
    REP(i, p.size()) {
        if (ccw(p[(i + n - 1) % n], p[i], p[(i + 1) % n]) == ON_SEG) {
            p.erase(p.begin() + i);
            i--;
        }
    }
    return p;
}

// 点a,bの垂直二等分線 O(1)
L bisector(P a, P b) {
    const P mid = (a + b) / P(2, 0);
    return L{mid, mid + (b - a) * P(0, 1)};
}
// 多角形polと点集合vについてボロノイ図を計算
// 点v[s]が属する部分を返す O(pol.size * v.size())
G voronoi_cell(G pol, G v, int s) {
    pol = normalize_poligon(pol);
    REP(i, v.size()) if (i != s) {
        pol = convex_cut(pol, bisector(v[s], v[i]));
    }
    return pol;
}

// 面積 頂点が反時計回りに並んでいること
R area(const G &pol) {
    R ret = 0.0;
    REP(i, pol.size()) ret += det(pol[i], pol[(i + 1) % pol.size()]);
    return (ret / 2.0);
}

// 凸性の判定
bool isConvex(const G &pol) {
    REP(i, pol.size()) {
        if (sgn(det(pol[(i + 1) % pol.size()] - pol[i],
                    pol[(i + 2) % pol.size()] - pol[i])) < 0) {
            return false;
        }
    }
    return true;
}

// 多角形と点の内包
// 2→in 1→on 0→out
int inPolygon(const G &pol, const P &p) {
    bool in = false;
    for (int i = 0; i < pol.size(); ++i) {
        P a = pol[i] - p, b = pol[(i + 1) % pol.size()] - p;
        if (imag(a) > imag(b))
            swap(a, b);
        if (imag(a) <= 0 && 0 < imag(b) && sgn(det(a, b)) < 0) {
            in = !in;
        }
        if (sgn(det(a, b)) == 0 && sgn(dot(a, b)) <= 0)
            return 1;
    }
    return in ? 2 : 0;
}
} // namespace Geometry

namespace std {
bool operator<(const Geometry::P &a, const Geometry::P &b) {
    return Geometry::sgn(real(a - b)) ? real(a - b) < 0
                                      : Geometry::sgn(imag(a - b)) < 0;
}
bool operator==(const Geometry::P &a, const Geometry::P &b) {
    return Geometry::sgn(real(a - b)) == 0 && Geometry::sgn(imag(a - b)) == 0;
}
bool cmp_y(const Geometry::P &a, const Geometry::P &b) {
    return Geometry::sgn(imag(a - b)) ? imag(a - b) < 0
                                      : Geometry::sgn(real(a - b)) < 0;
}
} // namespace std

vector<Geometry::G> vec[10];
vector<int> G[10];
int color[10];
int ans;

void dfs(int v, int n, int max_color) {
    if (v == n) {
        ans = min(ans, max_color);
        return;
    }
    if (ans <= max_color)
        return;
    bool used[10] = {};
    for (int to : G[v]) {
        if (to < v)
            used[color[to]] = 1;
    }
    for (int i = 0; i < max_color; i++) {
        if (!used[i]) {
            color[v] = i;
            dfs(v + 1, n, max_color);
        }
    }
    color[v] = max_color;
    dfs(v + 1, n, max_color + 1);
}

int main() {
    while (1) {
        int n;
        cin >> n;
        if (n == 0)
            break;
        for (int i = 0; i < 10; i++) {
            vec[i].clear();
            G[i].clear();
        }
        map<string, int> idx;
        int cnt = 0;
        for (int i = 0; i < n; i++) {
            string name;
            cin >> name;
            int c;
            if (idx.find(name) == idx.end()) {
                c = cnt++;
                idx[name] = c;
            } else {
                c = idx[name];
            }
            Geometry::G poly;
            while (1) {
                int x, y;
                cin >> x;
                if (x == -1)
                    break;
                cin >> y;
                poly.emplace_back(x, y);
            }
            vec[c].push_back(poly);
        }
        for (int i = 0; i < cnt; i++) {
            for (int j = i + 1; j < cnt; j++) {
                bool touch = 0;
                for (auto &A : vec[i]) {
                    for (auto &B : vec[j]) {
                        for (int k = 0; k < A.size(); k++) {
                            auto p1 = A[k];
                            auto p2 = A[(k + 1) % A.size()];
                            for (int l = 0; l < B.size(); l++) {
                                auto p3 = B[l];
                                auto p4 = B[(l + 1) % B.size()];
                                if (!Geometry::parallel(Geometry::S(p1, p2),
                                                        Geometry::S(p3, p4)))
                                    continue;
                                if (!Geometry::intersect(Geometry::S(p1, p2),
                                                         Geometry::S(p3, p4))) {
                                    continue;
                                }
                                if ((p3 == p1 || Geometry::ccw(p1, p2, p3) ==
                                                     Geometry::BACK) &&
                                    (p4 == p1 || Geometry::ccw(p1, p2, p4) ==
                                                     Geometry::BACK))
                                    continue;
                                if ((p3 == p2 || Geometry::ccw(p1, p2, p3) ==
                                                     Geometry::FRONT) &&
                                    (p4 == p2 || Geometry::ccw(p1, p2, p4) ==
                                                     Geometry::FRONT))
                                    continue;
                                touch = 1;
                                break;
                            }
                            if (touch)
                                break;
                        }
                        if (touch)
                            break;
                    }
                    if (touch)
                        break;
                }
                if (touch) {
                    G[i].push_back(j);
                    G[j].push_back(i);
                }
            }
        }
        ans = 1 << 30;
        dfs(0, cnt, 0);
        cout << ans << endl;
    }
}
accuracy: 1
time_ms: 10
memory_kb: 3376
final_rank: 1

code_snippet: #include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef pair<int, int> P;

#define eps 1e-9
#define INF 2000000000              // 2e9
#define LLINF 2000000000000000000ll // 2e18 (llmax:9e18)
#define all(x) (x).begin(), (x).end()
#define sq(x) ((x) * (x))

#ifndef LOCAL
#define dmp(...) ;
#else
#define dmp(...)                                                               \
  cerr << "[ " << #__VA_ARGS__ << " ] : " << dump_str(__VA_ARGS__) << endl
#endif

// ---------------- Utility ------------------

template <class T>
bool chmin(T &a, const T &b) {
  if (a <= b) return false;
  a = b;
  return true;
}
template <class T>
bool chmax(T &a, const T &b) {
  if (a >= b) return false;
  a = b;
  return true;
}

template <class T>
using MaxHeap = priority_queue<T>;
template <class T>
using MinHeap = priority_queue<T, vector<T>, greater<T>>;

template <class T>
vector<T> vect(int len, T elem) {
  return vector<T>(len, elem);
}

// ----------------- Input -------------------

template <class T, class U>
istream &operator>>(istream &is, pair<T, U> &p) {
  return is >> p.first >> p.second;
}

template <class T>
istream &operator>>(istream &is, vector<T> &vec) {
  for (int i = 0; i < vec.size(); i++) is >> vec[i];
  return is;
}

// ----------------- Output ------------------

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
  return os << p.first << ',' << p.second;
}

template <class T>
ostream &operator<<(ostream &os, const vector<T> &v) {
  for (const T &e : v) os << e << " ";
  return os;
}

template <class T>
ostream &operator<<(ostream &os, const deque<T> &d) {
  for (const T &e : d) os << e << " ";
  return os;
}

template <class T>
ostream &operator<<(ostream &os, const set<T> &s) {
  os << "{ ";
  for (const T &e : s) os << e << " ";
  return os << "}";
}

template <class T, class U>
ostream &operator<<(ostream &os, const map<T, U> &m) {
  os << "{ ";
  for (const auto &[key, val] : m) os << "( " << key << " -> " << val << " ) ";
  return os << "}";
}

void dump_str_rec(ostringstream &) {}

template <class Head, class... Tail>
void dump_str_rec(ostringstream &oss, Head &&head, Tail &&... tail) {
  oss << ", " << head;
  dump_str_rec(oss, forward<Tail>(tail)...);
}

template <class T, class... U>
string dump_str(T &&arg, U &&... args) {
  ostringstream oss;
  oss << arg;
  dump_str_rec(oss, forward<U>(args)...);
  return oss.str();
}

// --------------- Fast I/O ------------------

void fastio() {
  cin.tie(0);
  ios::sync_with_stdio(0);
  cout << fixed << setprecision(20);
}

// ------------ End of template --------------

#define endl "\n"

// Geometry Library
// written by okuraofvegetable

#define DOUBLE_INF 1e50
#define Points vector<Point>
#define EQ(a, b) (abs((a) - (b)) < eps)

inline double add(double a, double b) {
  if (abs(a + b) < eps * (abs(a) + abs(b))) return 0;
  return a + b;
}

// --------------------- Point -----------------------

#define Vector Point
struct Point {
  double x, y;
  Point() {}
  Point(double x, double y) : x(x), y(y) {}
  Point operator+(Point p) { return Point(add(x, p.x), add(y, p.y)); }
  Point operator-(Point p) { return Point(add(x, -p.x), add(y, -p.y)); }
  Point operator*(double d) { return Point(x * d, y * d); }
  Point operator/(double d) { return Point(x / d, y / d); }
  double dot(Point p) { return add(x * p.x, y * p.y); }
  double det(Point p) { return add(x * p.y, -y * p.x); }
  double norm() { return sqrt(x * x + y * y); }
  double norm2() { return x * x + y * y; }
  double dist(Point p) { return ((*this) - p).norm(); }
  double dist2(Point p) { return sq(x - p.x) + sq(y - p.y); }
  double arg() { return atan2(y, x); }             // [-PI,PI]
  double arg(Vector v) { return v.arg() - arg(); } // henkaku
  double angle(Vector v) {                         // [0,PI]
    return acos(cos(arg(v)));
  }
  Vector normalize() { return (*this) * (1.0 / norm()); }
  Point vert() { return Point(y, -x); }

  // Signed area of triange (0,0) (x,y) (p.x,p.y)
  double area(Point p) { return (x * p.y - p.x * y) / 2.0; }
  friend istream &operator>>(istream &is, Point &p) { return is >> p.x >> p.y; }
  friend ostream &operator<<(ostream &os, const Point &p) {
    return os << '(' << p.x << ',' << p.y << ')';
  }
};

Point divide_rate(Point a, Point b, double A, double B) {
  assert(!EQ(A + B, 0.0));
  return (a * B + b * A) * (1.0 / (A + B));
}

Vector polar(double len, double arg) {
  return Vector(cos(arg) * len, sin(arg) * len);
}

// Direction a -> b -> c
// verified AOJ CGL_1_C
enum { COUNTER_CLOCKWISE, CLOCKWISE, ONLINE_BACK, ONLINE_FRONT, ON_SEGMENT };
int ccw(Point a, Point b, Point c) {
  Vector p = b - a;
  Vector q = c - a;
  if (p.det(q) > 0.0) return COUNTER_CLOCKWISE; // counter clockwise
  if (p.det(q) < 0.0) return CLOCKWISE;         // clockwise
  if (p.dot(q) < 0.0) return ONLINE_BACK;       // c--a--b online_back
  if (p.norm() < q.norm()) return ONLINE_FRONT; // a--b--c online_front
  return ON_SEGMENT;                            // a--c--b on_segment
}

// --------------------- Line ------------------------
struct Line {
  Point a, b;
  Line() {}
  Line(Point a, Point b) : a(a), b(b) {}
  bool on(Point q) { return (a - q).det(b - q) == 0; }
  // folloing 2 functions verified AOJ CGL_2_A
  bool is_parallel(Line l) { return (a - b).det(l.a - l.b) == 0; }
  bool is_orthogonal(Line l) { return (a - b).dot(l.a - l.b) == 0; }
  Point intersection(Line l) {
    assert(!is_parallel(l));
    return a + (b - a) * ((l.b - l.a).det(l.a - a) / (l.b - l.a).det(b - a));
  }
  // Projection of p to this line
  // verified AOJ CGL_1_A
  Point projection(Point p) {
    return (b - a) * ((b - a).dot(p - a) / (b - a).norm2()) + a;
  }
  double distance(Point p) {
    Point q = projection(p);
    return p.dist(q);
  }
  // Reflection point of p onto this line
  // verified AOJ CGL_1_B
  Point refl(Point p) {
    Point proj = projection(p);
    return p + ((proj - p) * 2.0);
  }
  bool left(Point p) {
    if ((p - a).det(b - a) > 0.0)
      return true;
    else
      return false;
  }
  friend istream &operator>>(istream &is, Line &l) { return is >> l.a >> l.b; }
  friend ostream &operator<<(ostream &os, const Line &l) {
    return os << l.a << " -> " << l.b;
  }
};

// ------------------- Segment  ----------------------
struct Segment {
  Point a, b;
  Segment() {}
  Segment(Point a, Point b) : a(a), b(b) {}
  Line line() { return Line(a, b); }
  bool on(Point q) {
    return ((a - q).det(b - q) == 0 && (a - q).dot(b - q) <= 0);
  }
  bool on_strict(Point q) {
    return ((a - q).det(b - q) == 0 && (a - q).dot(b - q) < 0);
  }

  // verified AOJ CGL_2_B
  bool is_intersect(Segment s) {
    if (line().is_parallel(s.line())) {
      if (on(s.a) || on(s.b)) return true;
      if (s.on(a) || s.on(b)) return true;
      return false;
    }
    Point p = line().intersection(s.line());
    if (on(p) && s.on(p))
      return true;
    else
      return false;
  }
  bool is_intersect(Line l) {
    if (line().is_parallel(l)) {
      if (l.on(a) || l.on(b))
        return true;
      else
        return false;
    }
    Point p = line().intersection(l);
    if (on(p))
      return true;
    else
      return false;
  }
  // following 2 distance functions are verified at AOJ CGL_2_D
  double distance(Point p) {
    double res = DOUBLE_INF;
    Point q = line().projection(p);
    if (on(q)) res = min(res, p.dist(q));
    res = min(res, min(p.dist(a), p.dist(b)));
    return res;
  }
  double distance(Segment s) {
    if (is_intersect(s)) return 0.0;
    double res = DOUBLE_INF;
    res = min(res, s.distance(a));
    res = min(res, s.distance(b));
    res = min(res, this->distance(s.a));
    res = min(res, this->distance(s.b));
    return res;
  }
  friend istream &operator>>(istream &is, Segment &s) {
    return is >> s.a >> s.b;
  }
  friend ostream &operator<<(ostream &os, const Segment &s) {
    return os << s.a << " -> " << s.b;
  }
};

typedef vector<Point> Polygon;

bool solve() {
  int n;
  cin >> n;
  if (n == 0) return false;

  map<string, int> m;
  vector<Segment> ss;
  vector<int> ids;

  auto get_id = [&](string &s) {
    auto it = m.find(s);
    if (it != m.end()) return it->second;
    int id = m.size();
    return m[s] = id;
  };

  for (int i = 0; i < n; i++) {
    string s;
    cin >> s;
    int id = get_id(s);
    Polygon pol;
    while (1) {
      int x, y;
      cin >> x;
      if (x == -1) break;
      cin >> y;
      pol.push_back(Point(x, y));
    }
    int M = pol.size();
    for (int j = 0; j < M; j++) {
      ss.push_back(Segment(pol[j], pol[(j + 1) % M]));
      ids.push_back(id);
    }
  }

  auto adj = [](Segment s, Segment t) {
    if (!s.line().is_parallel(t.line())) return false;
    if (EQ(s.a.dist(t.a), 0) && EQ(s.b.dist(t.b), 0)) return true;
    if (EQ(s.a.dist(t.b), 0) && EQ(s.b.dist(t.a), 0)) return true;
    if (s.on_strict(t.a) || s.on_strict(t.b)) return true;
    if (t.on_strict(s.a) || t.on_strict(s.b)) return true;
    return false;
  };

  vector<vector<int>> g(m.size());

  auto add_edge = [&](int u, int v) {
    g[u].push_back(v);
    g[v].push_back(u);
  };

  for (int i = 0; i < ss.size(); i++) {
    for (int j = i + 1; j < ss.size(); j++) {
      if (ids[i] != ids[j] && adj(ss[i], ss[j])) add_edge(ids[i], ids[j]);
    }
  }

  dmp(m);

  for (int i = 0; i < g.size(); i++) {
    sort(all(g[i]));
    g[i].erase(unique(all(g[i])), g[i].end());
    dmp(g[i]);
  }

  int ans = INF;
  vector<int> col(g.size(), -1);

  function<void(int)> dfs = [&](int v) {
    if (v == g.size()) {
      int S = 0;
      for (int i = 0; i < g.size(); i++) {
        S |= (1 << col[i]);
        for (int u : g[i]) {
          if (col[i] == col[u]) return;
        }
      }
      chmin(ans, __builtin_popcount(S));
      return;
    } else {
      for (int i = 0; i <= v; i++) {
        col[v] = i;
        dfs(v + 1);
        col[v] = -1;
      }
    }
  };

  dfs(0);
  cout << ans << endl;

  return true;
}

int main() {
  fastio();
  while (solve()) {}
  // int t; cin >> t; while(t--)solve();

  // int t; cin >> t;
  // for(int i=1;i<=t;i++){
  //   cout << "Case #" << i << ": ";
  //   solve();
  // }
  return 0;
}
accuracy: 1
time_ms: 170
memory_kb: 3436
final_rank: 17

code_snippet: #line 2 "/Users/tiramister/CompetitiveProgramming/Cpp/CppLibrary/Geometry/geometry.hpp"

#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>

namespace geo {

using Real = long double;
constexpr Real EPS = 1e-10;
constexpr Real PI = 3.14159265358979323846L;

int cmp(Real a, Real b) {
    return std::abs(a - b) < EPS ? 0
           : a > b               ? 1
                                 : -1;
}

/* -------------------- Point -------------------- */
struct Point {
    Real x, y;

    explicit Point(Real x = 0, Real y = 0) : x(x), y(y) {}

    Point operator-() const { return Point(-x, -y); }

    Point operator+(const Point& p) const { return Point(*this) += p; }
    Point operator-(const Point& p) const { return Point(*this) -= p; }
    Point& operator+=(const Point& p) {
        x += p.x, y += p.y;
        return *this;
    }
    Point& operator-=(const Point& p) {
        x -= p.x, y -= p.y;
        return *this;
    }

    Point operator*(Real k) const { return Point(*this) *= k; }
    Point operator/(Real k) const { return Point(*this) /= k; }
    Point& operator*=(Real k) {
        x *= k, y *= k;
        return *this;
    }
    Point& operator/=(Real k) {
        x /= k, y /= k;
        return *this;
    }

    bool operator==(const Point& p) const { return cmp(x, p.x) == 0 && cmp(y, p.y) == 0; }
    bool operator!=(const Point& p) const { return !(*this == p); }
    bool operator<(const Point& p) const { return cmp(x, p.x) < 0 ||
                                                  (cmp(x, p.x) == 0 && cmp(y, p.y) < 0); }
    bool operator>(const Point& p) const { return cmp(x, p.x) > 0 ||
                                                  (cmp(x, p.x) == 0 && cmp(y, p.y) > 0); }

    friend std::istream& operator>>(std::istream& is, Point& p) {
        return is >> p.x >> p.y;
    }

    Real abs() const { return std::hypot(x, y); }
    Real arg() const { return std::atan2(y, x); }

    Point rotate(Real theta) const {
        return Point(x * std::cos(theta) - y * std::sin(theta),
                     x * std::sin(theta) + y * std::cos(theta));
    }
    Point normal() const { return Point(-y, x); }
    Point unit() const { return *this / abs(); }
};

Point polar(Real r, Real theta) { return Point(std::cos(theta), std::sin(theta)) * r; }

Real dist(const Point& p, const Point& q) { return (p - q).abs(); }
Real dot(const Point& p, const Point& q) { return p.x * q.x + p.y * q.y; }
Real cross(const Point& p, const Point& q) { return p.x * q.y - p.y * q.x; }

/* -------------------- Segment -------------------- */
struct Segment {
    Point p, q;

    explicit Segment(const Point& p = Point(), const Point& q = Point()) : p(p), q(q) {}

    friend std::istream& operator>>(std::istream& is, Segment& s) {
        return is >> s.p >> s.q;
    }

    Real length() const { return (p - q).abs(); }
    Point diff() const { return q - p; }
};

bool orthogonal(const Segment& s, const Segment& t) {
    return cmp(dot(s.diff(), t.diff()), 0) == 0;
}

bool parallel(const Segment& s, const Segment& t) {
    return cmp(cross(s.diff(), t.diff()), 0) == 0;
}

Point proj(const Segment& s, const Point& p) {
    auto v = s.diff().unit();
    return s.p + v * dot(v, p - s.p);
}

Point refl(const Segment& s, const Point& p) {
    auto t = proj(s, p);
    return t + (t - p);
}

// position of a point relative to a segment
enum Position {
    ON_SEGMENT = 0,
    COUNTER_CLOCKWISE = 1,
    CLOCKWISE = -1,
    ONLINE_FRONT = 2,
    ONLINE_BACK = -2
};

Position pos(const Segment& s, const Point& p) {
    auto t = Segment(s.p, p);

    auto c = cross(s.diff(), t.diff());
    if (cmp(c, 0) != 0) {
        return cmp(c, 0) > 0 ? COUNTER_CLOCKWISE : CLOCKWISE;
    }

    auto d = dot(s.diff(), t.diff());
    if (cmp(d, 0) < 0) return ONLINE_BACK;

    return cmp(t.length(), s.length()) > 0 ? ONLINE_FRONT : ON_SEGMENT;
}

// end: contain ends of segments
bool intersect(const Segment& s, const Segment& t, bool end) {
    return pos(s, t.p) * pos(s, t.q) < end &&
           pos(t, s.p) * pos(t, s.q) < end;
}

Point crosspoint(const Segment& s, const Segment& t) {
    auto c1 = cross(t.diff(), s.diff());
    auto c2 = cross(t.diff(), s.p - t.p);
    return s.p + s.diff() * (-c2 / c1);
}

Real dist(const Segment& s, const Point& p) {
    auto q = proj(s, p);
    if (pos(s, q) == ON_SEGMENT) return dist(p, q);
    return std::min(dist(p, s.p), dist(p, s.q));
}

Real dist(const Segment& s, const Segment& t) {
    if (intersect(s, t, true)) return 0;
    return std::min({dist(s, t.p), dist(s, t.q),
                     dist(t, s.p), dist(t, s.q)});
}

/* -------------------- Polygon -------------------- */
struct Polygon : public std::vector<Point> {
    using std::vector<Point>::vector;

    explicit Polygon(int n = 0) : std::vector<Point>(n) {}

    std::vector<Segment> segments() const {
        std::vector<Segment> segs;
        for (int i = 0; i < (int)size(); ++i) {
            segs.emplace_back((*this)[i], (*this)[(i + 1) % size()]);
        }
        return segs;
    }

    Real area() const {
        Real sum = 0;
        for (int i = 0; i < (int)size(); ++i) {
            sum += cross((*this)[i], (*this)[(i + 1) % size()]);
        }
        return std::abs(sum) / 2;
    }

    bool isconvex() const {
        for (int i = 0; i < (int)size(); ++i) {
            if (pos(Segment((*this)[i], (*this)[(i + 1) % size()]),
                    (*this)[(i + 2) % size()]) == CLOCKWISE) return false;
        }
        return true;
    }
};

// position of a point relative to a polygon
enum Contain {
    OUT,
    ON,
    IN
};

Contain contain(const Polygon& g, const Point& p) {
    bool in = false;
    for (int i = 0; i < (int)g.size(); ++i) {
        if (pos(Segment(g[i], g[(i + 1) % g.size()]), p) == ON_SEGMENT) return ON;

        auto a = g[i] - p, b = g[(i + 1) % g.size()] - p;
        if (a > b) std::swap(a, b);

        if (cmp(a.x, 0) <= 0 &&
            cmp(b.x, 0) > 0 &&
            cmp(cross(a, b), 0) < 0) in = !in;
    }
    return in ? IN : OUT;
}

// linear: choose colinear points
Polygon convexhull(Polygon& g, bool linear) {
    std::sort(g.begin(), g.end());
    int n = g.size();

    Polygon h(n * 2);
    int k = 0;

    for (int i = 0; i < n; ++i) {
        while (k >= 2 &&
               cmp(cross(h[k - 1] - h[k - 2], g[i] - h[k - 2]), 0) < !linear) {
            --k;
        }
        h[k++] = g[i];
    }

    int t = k + 1;
    for (int i = n - 2; i >= 0; --i) {
        while (k >= t &&
               cmp(cross(h[k - 1] - h[k - 2], g[i] - h[k - 2]), 0) < !linear) {
            --k;
        }
        h[k++] = g[i];
    }

    h.resize(k - 1);
    h.shrink_to_fit();
    return h;
}

// requirement: g is convex
Real diameter(const Polygon& g) {
    Real ret = 0;
    int j = 0;
    for (int i = 0; i < (int)g.size(); ++i) {
        while (j < (int)g.size() - 1 &&
               cmp(dist(g[i], g[j + 1]), dist(g[i], g[j])) > 0) ++j;
        ret = std::max(ret, dist(g[i], g[j]));
    }
    return ret;
}

// left side
// requirement: g is convex
Polygon convex_cut(const Polygon& g, const Segment& s) {
    Polygon h;
    for (int i = 0; i < (int)g.size(); ++i) {
        if (pos(s, g[i]) != CLOCKWISE) h.push_back(g[i]);

        Segment t(g[i], g[(i + 1) % g.size()]);
        if (pos(s, t.p) * pos(s, t.q) == -1) {
            h.push_back(crosspoint(s, t));
        }
    }
    return h;
}

// requirement: g is convex
bool intersect(const Polygon& g, const Segment& s) {
    auto area = convex_cut(g, s).area();
    return cmp(area, 0) != 0 && cmp(area, g.area()) != 0;
}

/* -------------------- Circle -------------------- */

struct Circle {
    Point p;
    Real r;
    explicit Circle(const Point& p = Point(), Real r = 0) : p(p), r(r) {}

    // center -> radius
    friend std::istream& operator>>(std::istream& is, Circle& a) {
        return is >> a.p >> a.r;
    }
};

// the number of common tangents
// requirement: a != b
int intersect(const Circle& a, const Circle& b) {
    auto d = dist(a.p, b.p);

    return cmp(d, a.r + b.r) > 0              ? 4
           : cmp(d, a.r + b.r) == 0           ? 3
           : cmp(d, std::abs(a.r - b.r)) > 0  ? 2
           : cmp(d, std::abs(a.r - b.r)) == 0 ? 1
                                              : 0;
}

// s is treated as a line
std::vector<Point> crosspoint(const Circle& a, const Segment& s) {
    std::vector<Point> ps;

    auto d = dist(proj(s, a.p), a.p);
    if (cmp(d, a.r) == 0) {
        ps.push_back(proj(s, a.p));

    } else if (cmp(d, a.r) < 0) {
        auto p = proj(s, a.p);
        auto v = s.q - s.p;
        v *= std::sqrt(a.r * a.r - d * d) / v.abs();

        ps.push_back(p + v);
        ps.push_back(p - v);
    }
    return ps;
}

// requirement: a != b
std::vector<Point> crosspoint(const Circle& a, const Circle& b) {
    std::vector<Point> ps;

    auto c = intersect(a, b);
    if (c == 0 || c == 4) return ps;

    auto d = dist(a.p, b.p);
    auto theta = std::acos((a.r * a.r + d * d - b.r * b.r) / (a.r * d * 2));
    auto phi = (b.p - a.p).arg();

    ps.push_back(a.p + polar(a.r, phi + theta));
    if (c == 2) ps.push_back(a.p + polar(a.r, phi - theta));

    return ps;
}

}  // namespace geo
#line 2 "main.cpp"

#line 5 "main.cpp"
#include <map>

bool intersect(const geo::Polygon& g, const geo::Polygon& h) {
    for (auto s : g.segments()) {
        for (auto t : h.segments()) {
            if (!geo::parallel(s, t)) continue;

            if (std::abs(geo::pos(s, t.p)) == 1) continue;  // not on line

            std::pair<geo::Real, geo::Real> sint(0, s.length()), tint;

            tint.first = geo::dot(s.diff().unit(), t.p - s.p);
            tint.second = geo::dot(s.diff().unit(), t.q - s.p);
            if (tint.first > tint.second) std::swap(tint.first, tint.second);

            if (geo::cmp(sint.first, tint.second) < 0 &&
                geo::cmp(tint.first, sint.second) < 0) return true;
        }
    }
    return false;
}

bool solve() {
    int m;
    std::cin >> m;
    if (m == 0) return false;

    std::vector<geo::Polygon> gs(m);
    std::vector<int> ids(m);

    std::map<std::string, int> mp;
    int n = 0;
    for (int i = 0; i < m; ++i) {
        std::string s;
        std::cin >> s;

        if (!mp.count(s)) mp[s] = n++;
        ids[i] = mp[s];

        auto& g = gs[i];
        while (true) {
            int x;
            std::cin >> x;
            if (x == -1) break;

            int y;
            std::cin >> y;
            g.emplace_back(x, y);
        }
    }

    std::vector<std::vector<int>> graph(n);
    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < i; ++j) {
            if (intersect(gs[i], gs[j])) {
                graph[ids[i]].push_back(ids[j]);
            }
        }
    }

    std::vector<bool> ok(1 << n, true);
    for (int b = 0; b < (1 << n); ++b) {
        for (int i = 0; i < n; ++i) {
            if ((~b >> i) & 1) continue;

            for (auto j : graph[i]) {
                if (i != j && ((b >> j) & 1)) ok[b] = false;
            }
        }
    }

    std::vector<int> ans(1 << n, n);
    ans[0] = 0;
    for (int b = 0; b < (1 << n); ++b) {
        for (int c = b; c > 0; c = (c - 1) & b) {
            if (ok[c]) ans[b] = std::min(ans[b], ans[b ^ c] + 1);
        }
    }

    std::cout << ans.back() << "\n";
    return true;
}

int main() {
    std::cin.tie(nullptr);
    std::ios::sync_with_stdio(false);

    while (solve()) {}

    return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 3396
final_rank: 9

code_snippet: #include <bits/stdc++.h>
using namespace std;
template<class T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return 1; } return 0; }
template<class T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return 1; } return 0; }

#define COUT(x) cout << #x << " = " << (x) << " (L" << __LINE__ << ")" << endl
template<class T1, class T2> ostream& operator << (ostream &s, pair<T1,T2> P)
{ return s << '<' << P.first << ", " << P.second << '>'; }
template<class T> ostream& operator << (ostream &s, vector<T> P)
{ for (int i = 0; i < P.size(); ++i) { if (i > 0) { s << " "; } s << P[i]; } return s; }
template<class T> ostream& operator << (ostream &s, vector<vector<T> > P)
{ for (int i = 0; i < P.size(); ++i) { s << endl << P[i]; } return s << endl; }
template<class T> ostream& operator << (ostream &s, set<T> P)
{ for(auto it : P) { s << "<" << it << "> "; } return s << endl; }
template<class T1, class T2> ostream& operator << (ostream &s, map<T1,T2> P)
{ for(auto it : P) { s << "<" << it.first << "->" << it.second << "> "; } return s << endl; }


// 彩色数
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;
	}
}
accuracy: 1
time_ms: 150
memory_kb: 3340
final_rank: 15

code_snippet: #include "iostream"
#include "climits"
#include "list"
#include "queue"
#include "stack"
#include "set"
#include "functional"
#include "algorithm"
#include "string"
#include "map"
#include "unordered_map"
#include "unordered_set"
#include "iomanip"
#include "cmath"
#include "random"
#include "bitset"
#include "cstdio"
#include "numeric"
#include "cassert"
#include "ctime"

using namespace std;

constexpr long long int MOD = 1000000007;
//constexpr int MOD = 1000000007;
//constexpr int MOD = 998244353;
//constexpr long long int MOD = 998244353;
constexpr double EPS = 1e-12;

//int N, M, K, T, H, W, L, R;
long long int N, M, K, T, H, W, L, R;

struct  Point {
	long double x;
	long double y;
	Point(long double xx, long double yy) {
		x = xx, y = yy;
	}
	Point() {
	}
	Point operator + (const Point& c)const {
		Point box;
		box.x = x + c.x;
		box.y = y + c.y;
		return box;
	}
	Point operator - (const Point& c)const {
		Point box;
		box.x = x - c.x;
		box.y = y - c.y;
		return box;
	}
};

struct Line {
	long double a, b, c;
	Line(const int aa, const int bb, const int cc) {
		a = aa, b = bb, c = cc;
	}
	Line(const Point s, const Point t) {
		if (abs(s.x - t.x) < EPS) {
			a = 1;
			b = 0;
		}
		else {
			b = 1;
			a = (t.y - s.y) / (s.x - t.x);
		}
		c = s.x*a + s.y*b;
	}
};

long double dot(Point a, Point b) {
	return a.x*b.x + a.y*b.y;
}

long double cross(Point a, Point b) {
	return a.x*b.y - b.x*a.y;
}

long double Distance(Point a, Point b) {
	return sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y));
}

long double Distance(Point a, pair<Point, Point>b) {
	Line l = Line(b.first, b.second);
	if (abs(l.a*a.x + l.b*a.y - l.c) < EPS) {
		return min(Distance(a, b.first), Distance(a, b.second));
	}
	long double A = pow((b.first.x - b.second.x), 2) + pow((b.first.y - b.second.y), 2);
	long double B = 2 * (a.x - b.first.x)*(b.first.x - b.second.x) + 2 * (a.y - b.first.y)*(b.first.y - b.second.y);
	long double r = B / 2 / A;
	r = -r;
	if (r >= 0 && r <= 1) {
		return Distance(a, Point(b.first.x + r * (b.second.x - b.first.x), b.first.y + r * (b.second.y - b.first.y)));
	}
	else {
		return min(Distance(a, b.first), Distance(a, b.second));
	}
}

bool LineCross(Point a1, Point a2, Point b1, Point b2) {
	return cross(a2 - a1, b1 - a1)*cross(a2 - a1, b2 - a1) < EPS&&cross(b2 - b1, a1 - b1)*cross(b2 - b1, a2 - b1) < EPS && !(abs((a1 - a2).x*(a1 - b1).y - (a1 - a2).y*(a1 - b1).x)<EPS && abs((a1 - a2).x*(a1 - b2).y - (a1 - a2).y*(a1 - b2).x)<EPS&&abs(Distance(a1, a2) - Distance(a1, b1) - Distance(a2, b1)) > EPS&&abs(Distance(a1, a2) - Distance(a2, b2) - Distance(a1, b2)) > EPS&&abs(Distance(b1, b2) - Distance(a1, b1) - Distance(a1, b2)) > EPS&&abs(Distance(b1, b2) - Distance(a2, b1) - Distance(a2, b2)) > EPS);
}

long double Distance(pair<Point, Point>a, pair<Point, Point>b) {
	if (LineCross(a.first, a.second, b.first, b.second))return 0;
	return min({ Distance(a.first,b),Distance(a.second,b),Distance(b.first,a),Distance(b.second,a) });
}

Point LineCross(Line a, Line b) {
	Point ret;
	ret.x = (a.c*b.b - a.b*b.c) / (a.a*b.b - a.b*b.a);
	ret.y = -(a.c*b.a - a.a*b.c) / (a.a*b.b - a.b*b.a);
	return ret;
}

long double TriangleArea(Point a, Point b, Point c) {
	long double A, B, C;
	A = Distance(a, b);
	B = Distance(a, c);
	C = Distance(b, c);
	long double S = (A + B + C) / 2;
	return sqrt(S*(S - A)*(S - B)*(S - C));
}

bool IsPointLeft(pair<Point, Point>a, Point b) {
	a.second = a.second - a.first;
	b = b - a.first;
	a.first = a.first - a.first;
	long double radline = atan2(a.second.y, a.second.x);
	long double radpoint = atan2(b.y, b.x) - radline;
	if (sin(radpoint) >= -EPS)return true;
	else return false;
}

bool IsPointInPolygon(Point p, vector<Point>poly) {
	bool ret = true;
	for (int i = 0; i < poly.size(); i++) {
		if (Distance(p, { poly[i],poly[(i + 1) % poly.size()] }) <= EPS) return true;
		ret &= IsPointLeft({ poly[i],poly[(i + 1) % poly.size()] }, p);
	}
	return ret;
}

long double Area(vector<Point>p) {
	long double ret = 0;
	for (int i = 2; i < p.size(); i++) {
		ret += TriangleArea(p[0], p[i - 1], p[i]);
	}
	return ret;
}

vector<vector<Point>>DividePolygonByLine(vector<Point>p, pair<Point, Point>l) {
	vector<int>cr;
	vector<Point>cp;
	for (int k = 0; k < p.size(); k++) {
		int nx = k + 1;
		nx %= p.size();
		if (LineCross(p[k], p[(k + 1) % p.size()], l.first, l.second)) {
			auto np = LineCross(Line(p[k], p[(k + 1) % p.size()]), Line(l.first, l.second));
			bool flag = true;
			for (auto l : cp) {
				if (Distance(l, np) <= EPS)flag = false;
			}
			if (flag) {
				cr.push_back(k);
				cp.push_back(np);
			}
		}
	}
	vector<Point>w;
	vector<Point>x;
	if (cr.size() != 2)return vector<vector<Point>>(1, p);
	int cnt = cr[0];
	do {
		if (w.empty() || Distance(w.back(), p[cnt]) > EPS)w.push_back(p[cnt]);
		if (cnt == cr[0]) {
			if (w.empty() || Distance(w.back(), cp[0]) > EPS)w.push_back(cp[0]);
			if (w.empty() || Distance(w.back(), cp[1]) > EPS)w.push_back(cp[1]);
			cnt = cr[1] + 1;
			cnt %= p.size();
		}
		else if (cnt == cr[1]) {
			if (w.empty() || Distance(w.back(), cp[1]) > EPS)w.push_back(cp[1]);
			if (w.empty() || Distance(w.back(), cp[0]) > EPS)w.push_back(cp[0]);
			cnt = cr[0] + 1;
			cnt %= p.size();
		}
		else {
cnt++;
cnt %= p.size();
		}
	} while (cnt != cr[0]);
	cnt = cr[1];
	do {
		if (x.empty() || Distance(x.back(), p[cnt]) > EPS)x.push_back(p[cnt]);
		if (cnt == cr[0]) {
			if (x.empty() || Distance(x.back(), cp[0]) > EPS)x.push_back(cp[0]);
			if (x.empty() || Distance(x.back(), cp[1]) > EPS)x.push_back(cp[1]);
			cnt = cr[1] + 1;
			cnt %= p.size();
		}
		else if (cnt == cr[1]) {
			if (x.empty() || Distance(x.back(), cp[1]) > EPS)x.push_back(cp[1]);
			if (x.empty() || Distance(x.back(), cp[0]) > EPS)x.push_back(cp[0]);
			cnt = cr[0] + 1;
			cnt %= p.size();
		}
		else {
			cnt++;
			cnt %= p.size();
		}
	} while (cnt != cr[1]);
	vector<vector<Point>>ret;
	ret.push_back(w);
	ret.push_back(x);
	return ret;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);

	vector<int>ret;
	long double pi = acos(-1);
	while (cin >> N, N) {
		vector<string>name(N);
		vector<vector<Point>>vp(N);
		map<string, int>mp;
		for (int i = 0; i < N; i++) {
			cin >> name[i];
			mp[name[i]] = 0;
			long double x, y;
			while (cin >> x, x > -0.5) {
				cin >> y;
				vp[i].push_back(Point(x, y));
			}
		}
		int cnt = 0;
		for (auto &i : mp)i.second = cnt++;
		vector<vector<int>>edge(mp.size(), vector<int>(mp.size()));
		for (int i = 0; i < N; i++) {
			for (int j = i + 1; j < N; j++) {
				for (int k = 0; k < vp[i].size(); k++) {
					int nk = k + 1;
					nk %= vp[i].size();
					for (int l = 0; l < vp[j].size(); l++) {
						int nl = l + 1;
						nl %= vp[j].size();
						long double rada = atan2(vp[i][nk].y - vp[i][k].y, vp[i][nk].x - vp[i][k].x);
						long double radb = atan2(vp[j][nl].y - vp[j][l].y, vp[j][nl].x - vp[j][l].x);
						//	cout << setprecision(20) << "ijkl rad " << i << " " << j << " " << k << " " << l << " " << rada << " " << radb << endl;
						if (abs(rada - radb) >= EPS && abs(pi * 2 - abs(rada - radb)) > EPS&&abs(pi - abs(rada - radb)) > EPS&&abs(pi * 3 - abs(rada - radb)) > EPS) continue;
						if (Distance({ vp[i][k],vp[i][nk] }, { vp[j][l],vp[j][nl] }) > EPS)continue;
						//	cout << "ijkl " << i << " " << j << " " << k << " " << l << endl;
						long double alen = Distance(vp[i][k], vp[i][nk]);
						long double blen = Distance(vp[j][l], vp[j][nl]);
						vector<long double>dis;
						dis.push_back(Distance(vp[i][k], vp[j][l]));
						dis.push_back(Distance(vp[i][k], vp[j][nl]));
						dis.push_back(Distance(vp[i][nk], vp[j][l]));
						dis.push_back(Distance(vp[i][nk], vp[j][nl]));
						sort(dis.begin(), dis.end());
						//	for (auto m : dis)cout << m << " ";
						//	cout << endl;
						//	cout << alen << " " << blen << endl;
						if (dis.front() < EPS&&dis.back() >= alen + blen - EPS)continue;
						edge[mp[name[i]]][mp[name[j]]] = 1;
						edge[mp[name[j]]][mp[name[i]]] = 1;
						//	cout << name[i] << " " << name[j] << endl;
					}
				}
			}
		}
		int ans = MOD;
		vector<int>flag(1 << mp.size());
		vector<int>dp(1 << mp.size(), MOD);
		dp[0] = 0;
		for (int i = 0; i < 1 << mp.size(); i++) {
			bool ok = true;
			for (int j = 0; j < mp.size(); j++) {
				if ((i >> j) % 2 == 0)continue;
				for (int k = j + 1; k < mp.size(); k++) {
					if ((i >> k) % 2 == 0)continue;
					if (edge[j][k])ok = false;
				}
			}
			flag[i] = ok;
		}
		for (int i = 0; i <1<< mp.size(); i++) {
			for (int j = 0; j <1<< mp.size(); j++) {
				if (i&j)continue;
				if (flag[j] == 0)continue;
				dp[i + j] = min(dp[i + j], dp[i] + 1);
			}
		}
		ret.push_back(dp.back());
	}
	for (auto i : ret)cout << i << endl;
}
accuracy: 1
time_ms: 120
memory_kb: 3228
final_rank: 14