Problem:
Problem Statement
You have just transferred to another world, and got a map of this world.
There are several countries in this world.
Each country has a connected territory,
which is drawn on the map as a simple polygon consisting of its border segments in the $2$-dimensional plane.
You are strange to this world, so you would like to paint countries on the map to distinguish them.
If you paint adjacent countries with same color, it would be rather difficult to distinguish the countries.
Therefore, you want to paint adjacent countries with different colors.
Here, we define two countries are adjacent if their borders have at least one common segment whose length is strictly greater than $0$.
Note that two countries are NOT considered adjacent if the borders only touch at points.
Because you don't have the currency of this world, it is hard for you to prepare many colors.
What is the minimum number of colors to paint the map such that adjacent countries can be painted with different colors?
Input
The input consists of multiple datasets. The number of dataset is no more than $35$.
Each dataset is formatted as follows.
$n$
$m_1$
$x_{1,1}$ $y_{1,1}$
:
:
$x_{1,m_1}$ $y_{1,m_1}$
:
:
$m_n$
$x_{n,1}$ $y_{n,1}$
:
:
$x_{n,m_n}$ $y_{n,m_n}$
The first line of each dataset contains an integer $n$ ($1 \le n \le 35$),
which denotes the number of countries in this world.
The rest of each dataset describes the information of $n$ polygons representing the countries.
The first line of the information of the $i$-th polygon contains an integer $m_i$ ($3 \le m_i \le 50$), which denotes the number of vertices.
The following $m_i$ lines describe the coordinates of the vertices in the counter-clockwise order.
The $j$-th line of them contains two integers $x_{i,j}$ and $y_{i,j}$ ($|x_{i,j}|, |y_{i,j}| \le 10^3$),
which denote the coordinates of the $j$-th vertex of the $i$-th polygon.
You can assume the followings.
Each polygon has an area greater than $0$.
Two vertices of the same polygon have distinct coordinates.
Two segments of the same polygon do not have any common points except that exactly two segments meet at each vertex.
Two polygons have no common area.
The end of input is indicated by a line containing a single zero.
Output
For each dataset, output the minimum number of colors to paint the map such that adjacent countries are painted with different colors in a line.
Sample Input
1
3
0 0
1 0
0 1
4
4
0 0
10 0
10 10
0 10
4
10 0
20 0
20 10
10 10
4
0 10
10 10
10 20
0 20
4
10 10
20 10
20 20
10 20
3
4
-10 -10
2 2
10 10
-11 7
3
-1 -1
1 -1
0 0
3
0 0
3 -3
20 20
7
4
46 12
52 12
53 15
45 15
32
67 1
70 0
73 1
77 3
79 5
80 8
77 8
76 5
74 4
71 3
70 3
67 4
65 6
63 8
62 14
64 19
66 21
70 22
75 21
78 16
80 16
80 17
79 20
78 22
74 24
67 24
63 22
61 19
60 15
60 10
62 5
64 3
5
74 14
80 14
80 16
78 16
74 16
19
34 0
37 0
37 19
36 22
35 23
32 24
30 24
27 24
25 23
23 20
23 18
23 15
26 15
26 18
27 20
29 21
32 21
34 20
34 18
4
47 0
50 0
42 24
39 24
4
79 20
80 17
80 22
78 22
4
50 0
58 24
5
...(truncated)

Submitted solutions:

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

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

struct point{
    ll x,y;
};

bool is_adj(point p1,point p2,point p3,point p4){
    if((p2.y-p1.y)*(p4.x-p3.x)!=(p2.x-p1.x)*(p4.y-p3.y))return 0;
    ll LHS=-p1.x*(p2.y-p1.y)*(p4.x-p3.x)+p1.y*(p2.x-p1.x)*(p4.x-p3.x);
    ll RHS=-p3.x*(p4.y-p3.y)*(p2.x-p1.x)+p3.y*(p4.x-p3.x)*(p2.x-p1.x);
    if(LHS!=RHS)return 0;

    if(p1.x==p2.x){
        if(p1.x!=p3.x)return 0;
        if(p1.y>p2.y)swap(p1,p2);
        if(p3.y>p4.y)swap(p3,p4);
        if(max(p1.y,p3.y)<min(p2.y,p4.y))return 1;
    }
    if(p1.x>p2.x)swap(p1,p2);
    if(p3.x>p4.x)swap(p3,p4);
    if(max(p1.x,p3.x)<min(p2.x,p4.x))return 1;
    return 0;
}

bool is_bip(vector<vector<bool>> ADJ){
    int n=ADJ.size();
    vector<int> seen(n,-1);
    rep(i,n){
        if(seen[i]>=0)continue;
        seen[i]=0;
        queue<int> Q;
        Q.push(i);
        while(!Q.empty()){
            int p=Q.front();
            Q.pop();
            rep(j,n){
                if(ADJ[p][j]){
                    if(seen[j]==-1){
                        seen[j]=1-seen[p];
                        Q.push(j);
                    }
                    if(seen[j]==seen[p])return 0;
                }
            }
        }
    }
    return 1;
}

using vi=vector<int>;
using vvi=vector<vi>;
struct SCC {
  int n;
  vi cmp, order, use;
  vvi g, rg, group;

  SCC(int n, vector<pair<int, int>> edge)
      : n(n), g(n), rg(n), cmp(n, -1), use(n) {
    for(auto [frm, to] : edge) {
      g[frm].push_back(to);
      rg[to].push_back(frm);
    }
    rep(i, n) dfs(i);
    int c = 0;
    rep(i, n) c += rdfs(order[n - 1 - i], c);
    group.resize(c);
    rep(i, n) group[cmp[i]].push_back(i);
  }

  void dfs(int v) {
    if(use[v]) return;
    use[v] = 1;
    for(auto to : g[v]) dfs(to);
    order.push_back(v);
  }

  int rdfs(int v, int c) {
    if(cmp[v] != -1) return 0;
    cmp[v] = c;
    for(auto to : rg[v]) rdfs(to, c);
    return 1;
  }
};

struct two_sat {
  int n;
  vector<pair<int, int>> edge;

  two_sat(int n) : n(n) {}

  void add_clause(int i, int f, int j, int g) {
    edge.push_back({2 * i + (1 - f), 2 * j + g});
    edge.push_back({2 * j + (1 - g), 2 * i + f});
  }

  vi calc() {
    SCC scc(2 * n, edge);
    vvi group = scc.group;
    vi res(2 * n);
    rep(i, group.size()) {
      for(auto j : group[i]) res[j] = i;
    }
    vi ans(n);
    rep(i, n) {
      if(res[2 * i] == res[2 * i + 1]) return {};
      ans[i] = (res[2 * i] < res[2 * i + 1]) ? 1 : 0;
    }
    return ans;
  }
};

vector<vector<int>> IS;
std::vector<int> independent_set(
    const std::vector<std::vector<int>>& G) {
    const int n = G.size();
    std::vector<bool> used(n), ans(n);
    std::vector<int> deg(n), dead(n);
    std::ranges::transform(G, deg.begin(), [&](auto& g) { return g.size(); });
    int res = 0, cnt = 0, alive = n;

    auto dfs = [&](const auto& dfs) {
        // IS.push_back({});
        // rep(i,n)if(used[i])IS.back().push_back(i);
        int v = -1;
        for (int i = 0; i < n; ++i) {
            if (used[i] || dead[i]) continue;
            if (deg[i] <= 1) {
                v = i;
                break;
            }
            if (v < 0 || deg[v] < deg[i]) v = i;
        }
        if (v < 0) {
            // cout<<"HERE"<<endl;
            IS.push_back({});
            rep(i,n)if(used[i])IS.back().emplace_back(i);
            return;
        }

        // not use
        if (deg[v] != 1) {
            dead[v] = true;
            --alive;
            for (int u : G[v]) --deg[u];

            dfs(dfs);

            dead[v] = false;
            ++alive;
            for (int u : G[v]) ++deg[u];
        }

        // use
        used[v] = true;
        --alive;
        for (int u : G[v]) {
            if (!dead[u]) --alive;
            ++dead[u];
        }
        ++cnt;

        if (res < cnt) {
            ans = used;
            res = cnt;
        }

        dfs(dfs);

        used[v] = false;
        ++alive;
        for (int u : G[v]) {
            --dead[u];
            if (!dead[u]) ++alive;
        }
        --cnt;
    };

    dfs(dfs);

    std::vector<int> ret;
    for (int i = 0; i < n; ++i) {
        if (ans[i]) ret.push_back(i);
    }
    return ret;
}

ll ans(vector<vector<bool>> ADJ){
    clock_t start=clock(),end=clock();
    int n=ADJ.size();
    if(n==0)return 0;
    else if(n==1)return 1;
    bool ans_one=1;
    rep(i,n)rep(j,n)if(ADJ[i][j])ans_one=0;
    if(ans_one)return 1;
    if(is_bip(ADJ))return 2;
    rep(i,n)rep(j,i)rep(k,j)rep(e,k){
        if(ADJ[i][j]&&ADJ[i][k]&&ADJ[i][e]&&ADJ[j][k]&&ADJ[j][e]&ADJ[k][e])return 4;
    }

    vector<vector<int>> G(n);
    rep(i,n)rep(j,n)if(ADJ[i][j])G[i].push_back(j);
    IS={};
    auto RES=independent_set(G);


    // cout<<"sz "<<IS.size()<<endl;
    
    int ISN=IS.size();
    rep(j,ISN){
      
        vector<vector<bool>> D(n,vector<bool>(n,0));
        vector<int> A(n,1);
        for(int i:IS[j]){
            A[i]=0;
        }
        rep(i,n)rep(j,n){
            if(ADJ[i][j]&&A[i]+A[j]==2){
              D[i][j]=1;
            }
        }
        if(is_bip(D))return 3;
    }
    
    return 4;
        
}

void solve(int N){
    vector<int> A(N);
    vector<vector<point>> Poly(N);
    map<string,int> M;
    rep(i,N){
        // string s;
        // cin>>s;
        // if(!M.count(s))M[s]=M.size();
        A[i]=i;
        ll x, y;
        int k;
        cin>>k;
        rep(j,k){
            cin>>x>>y;
            Poly[i].push_back(point({x,y}));
        }
    }
    int CN=A.size();
    vector<vector<bool>> ADJ(CN,vector<bool>(CN,0));
    rep(i,N)rep(j,i){
        if(A[i]==A[j])continue;
        int n=Poly[i].size();
        int m=Poly[j].size();
        rep(k,n)rep(l,m){
            if(is_adj(Poly[i][k],Poly[i][(k+1)%n],Poly[j][l],Poly[j][(l+1)%m])){
                ADJ[A[i]][A[j]]=ADJ[A[j]][A[i]]=1;
            }
        }
    }
    // rep(i,CN){
    //     rep(j,CN)cout<<ADJ[i][j];
    //     cout<<endl;
    // }
    cout<<ans(ADJ)<<endl;
    // cout<<endl;
}

int main() {
    
    int N;
    while(cin>>N,N!=0)solve(N);

}
accuracy: 1
time_ms: 680
memory_kb: 7928
final_rank: 3

code_snippet: #pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#include<iostream>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
#include<functional>
#include<iomanip>
#include<queue>
#include<ciso646>
#include<random>
#include<map>
#include<set>
#include<bitset>
#include<stack>
#include<unordered_map>
#include<unordered_set>
#include<utility>
#include<cassert>
#include<complex>
#include<numeric>
#include<array>
#include<chrono>
using namespace std;

//#define int long long
typedef long long ll;

typedef unsigned long long ul;
typedef unsigned int ui;
constexpr ll mod = 1000000007;
const ll INF = mod * mod;
typedef pair<int, int>P;

#define rep(i,n) for(int i=0;i<n;i++)
#define per(i,n) for(int i=n-1;i>=0;i--)
#define Rep(i,sta,n) for(int i=sta;i<n;i++)
#define rep1(i,n) for(int i=1;i<=n;i++)
#define per1(i,n) for(int i=n;i>=1;i--)
#define Rep1(i,sta,n) for(int i=sta;i<=n;i++)
#define all(v) (v).begin(),(v).end()
typedef pair<ll, ll> LP;

template<typename T>
void chmin(T& a, T b) {
	a = min(a, b);
}
template<typename T>
void chmax(T& a, T b) {
	a = max(a, b);
}
template<typename T>
void cinarray(vector<T>& v) {
	rep(i, v.size())cin >> v[i];
}
template<typename T>
void coutarray(vector<T>& v) {
	rep(i, v.size()) {
		if (i > 0)cout << " "; cout << v[i];
	}
	cout << "\n";
}
ll mod_pow(ll x, ll n, ll m = mod) {
	if (n < 0) {
		ll res = mod_pow(x, -n, m);
		return mod_pow(res, m - 2, m);
	}
	if (abs(x) >= m)x %= m;
	if (x < 0)x += m;
	if (x == 0)return 0;
	ll res = 1;
	while (n) {
		if (n & 1)res = res * x % m;
		x = x * x % m; n >>= 1;
	}
	return res;
}
struct modint {
	int n;
	modint() :n(0) { ; }
	modint(ll m) {
		if (m < 0 || mod <= m) {
			m %= mod; if (m < 0)m += mod;
		}
		n = m;
	}
	operator int() { return n; }
};
bool operator==(modint a, modint b) { return a.n == b.n; }
modint operator+=(modint& a, modint b) { a.n += b.n; if (a.n >= mod)a.n -= mod; return a; }
modint operator-=(modint& a, modint b) { a.n -= b.n; if (a.n < 0)a.n += mod; return a; }
modint operator*=(modint& a, modint b) { a.n = ((ll)a.n * b.n) % mod; return a; }
modint operator+(modint a, modint b) { return a += b; }
modint operator-(modint a, modint b) { return a -= b; }
modint operator*(modint a, modint b) { return a *= b; }
modint operator^(modint a, ll n) {
	if (n == 0)return modint(1);
	modint res = (a * a) ^ (n / 2);
	if (n % 2)res = res * a;
	return res;
}

ll inv(ll a, ll p) {
	return (a == 1 ? 1 : (1 - p * inv(p % a, a)) / a + p);
}
modint operator/(modint a, modint b) { return a * modint(inv(b, mod)); }
modint operator/=(modint& a, modint b) { a = a / b; return a; }
const int max_n = 1 << 10;
modint fact[max_n], factinv[max_n];
void init_f() {
	fact[0] = modint(1);
	for (int i = 0; i < max_n - 1; i++) {
		fact[i + 1] = fact[i] * modint(i + 1);
	}
	factinv[max_n - 1] = modint(1) / fact[max_n - 1];
	for (int i = max_n - 2; i >= 0; i--) {
		factinv[i] = factinv[i + 1] * modint(i + 1);
	}
}
modint comb(int a, int b) {
	if (a < 0 || b < 0 || a < b)return 0;
	return fact[a] * factinv[b] * factinv[a - b];
}
modint combP(int a, int b) {
	if (a < 0 || b < 0 || a < b)return 0;
	return fact[a] * factinv[a - b];
}

ll gcd(ll a, ll b) {
	a = abs(a); b = abs(b);
	if (a < b)swap(a, b);
	while (b) {
		ll r = a % b; a = b; b = r;
	}
	return a;
}
typedef long double ld;
typedef pair<ld, ld> LDP;
const ld eps = 1e-8;
const ld pi = acosl(-1.0);
int dx[4] = { 1,0,-1,0 };
int dy[4] = { 0,1,0,-1 };

using mP = pair<modint, modint>;
//-----------------------------------

ll dot(P a, P b) {
	return a.first * b.first + a.second * b.second;
}
ll cross(P a, P b) {
	return a.first * b.second - a.second * b.first;
}
ll norm2(P a) {
	return a.first * a.first + a.second * a.second;
}
int ccw(P a, P b, P c) {
	b.first -= a.first; b.second -= a.second;
	c.first -= a.first; c.second -= a.second;
	if (cross(b, c) > 0)return 1;//counter clockwise
	if (cross(b, c) < 0)return -1;//clockwise
	if (dot(b, c) < 0)return 2;//c--a--b on line
	if (norm2(b) < norm2(c))return -2;//a--b--c on line
	return 0;//a--c--b on line
}

bool isadj(P p1, P p2, P q1, P q2) {
	if (abs(ccw(p1, p2, q1)) == 1)return false;
	if (p1 > p2)swap(p1, p2);
	if (q1 > q2)swap(q1, q2);
	int dx1 = p2.first - p1.first;
	int dy1 = p2.second - p1.second;
	int dx2 = q2.first - q1.first;
	int dy2 = q2.second - q1.second;
	if (dx1 * dy2 != dx2 * dy1)return false;
	if (dx1 == 0) {
		int ly = max(p1.second, q1.second);
		int ry = min(p2.second, q2.second);
		return ly < ry;
	}
	int lx = max(p1.first, q1.first);
	int rx = min(p2.first, q2.first);
	return lx < rx;
}

int n;
bool exi[35][35];
void solve() {
	vector<vector<P>> v(n);
	rep(i, n) {
		int m; cin >> m;
		v[i].resize(m);
		rep(j, m) {
			int x, y; cin >> x >> y;
			v[i][j] = { x,y };
		}
	}
	//cout << "?\n";
	//cout << "ans is ";
	rep(i, n)rep(j, n)if(i!=j) {
		exi[i][j] = false;
		rep(a, v[i].size())rep(b, v[j].size()) {
			int na = a + 1 == v[i].size() ? 0 : a + 1;
			int nb = b + 1 == v[j].size() ? 0 : b + 1;
			if (isadj(v[i][a], v[i][na], v[j][b], v[j][nb]))exi[i][j] = true;
		}
		//if (exi[i][j])cout << i << " " << j << "\n";
	}
	//1?
	bool exi1 = false;
	rep(i, n)rep(j, n)if (exi[i][j])exi1 = true;
	if (!exi1) {
		cout << 1 << "\n"; return;
	}
	//2?
	bool valid2 = true;
	vector<bool> used(n);
	vector<int> col(n);
	rep(i, n) {
		if (used[i])continue;
		queue<int> q;
		q.push(i); used[i] = true; col[i] = 0;
		while (!q.empty()) {
			int id = q.front(); q.pop();
			rep(to, n)if (exi[id][to]) {
				if (used[to]) {
					if (col[to] != (col[id] ^ 1))valid2 = false;
				}
				else {
					used[to] = true;
					q.push(to);
					col[to] = col[id] ^ 1;
				}
			}
		}
	}
	if (valid2) {
		cout << 2 << "\n"; return;
	}
	//erase degree<=2
	while (true) {
		bool upd = false;
		rep(i, n) {
			int cnt = 0;
			rep(j, n)if (exi[i][j])cnt++;
			if (0<cnt&&cnt <= 2) {
				upd = true;
				rep(j, n)if (exi[i][j]) {
					exi[i][j] = exi[j][i] = false;
				}
			}
		}
		if (!upd)break;
	}
	int ans = 4;
	vector<bool> ban(n);
	vector<int> rest(n);
	rep(i, n)Rep(j, i + 1, n)if (exi[i][j])rest[i]++;
	vector<vector<int>> G(n);
	rep(i, n)rep(j, n)if (exi[i][j])G[i].push_back(j);
	queue<int> q;
	function<void(int)>dfs = [&](int loc) {
		if (loc == n) {
			//check
			//coutarray(ban);
			fill(all(used), false);
			bool valid = true;
			rep(i, n) {
				if (used[i])continue;
				if (ban[i])continue;
				q.push(i); used[i] = true;
				while (!q.empty()) {
					int id = q.front(); q.pop();
					for (int to : G[id]) {
						if (ban[to])continue;
						if (used[to]) {
							if (col[to] != (col[id] ^ 1))valid = false;
						}
						else {
							used[to] = true;
							col[to] = col[id] ^ 1;
							q.push(to);
						}
					}
				}
			}
			if (valid) {
				ans = 3;
			}

			return;
		}
		bool preexi = false;
		rep(i, loc)if(exi[i][loc]) {
			if (ban[i])preexi = true;
		}
		if (preexi) {
			dfs(loc + 1);
		}
		else {
			ban[loc] = true;
			dfs(loc + 1);
			ban[loc] = false;
			if (rest[loc] > 0) {
				dfs(loc + 1);
			}
		}
	};
	dfs(0);
	cout << ans << "\n";
}


signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << fixed << setprecision(10);
	//init_f();
	//init();
	//while(true)
	//expr();
	//int t; cin >> t; rep(i, t)
	while(cin>>n,n)
	solve();
	return 0;
}
accuracy: 1
time_ms: 2800
memory_kb: 3192
final_rank: 7

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 99999999999999999
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;



#define SIZE 40

enum Type{
	UNDEFINED,
	A,
	B,
	C,
};

struct Point{
	Point(double arg_x,double arg_y){
		x = arg_x;
		y = arg_y;
	}

	Point(){
		x = y = 0.0;
	}

	Point operator + (Point p){ return Point(x+p.x,y+p.y); }
	Point operator - (Point p){ return Point(x-p.x,y-p.y);}
	Point operator * (double a){ return Point(a*x,a*y); }
	Point operator / (double a){ return Point(x/a,y/a); }

	double abs(){ return sqrt(norm()); }
	double norm(){ return x*x + y*y; }

	bool operator<(const Point &p) const{
		return x != p.x? x < p.x: y < p.y;
	}

	bool operator == (const Point &p) const{
		return fabs(x-p.x) < EPS && fabs(y-p.y) < EPS;
	}

	double x,y;
};

typedef Point Vector;
typedef vector<Point> Polygon;

struct Line{
	Line(){

	}
	Line(Point a,Point b){
		p[0] = a;
		p[1] = b;
	}
	Point p[2];
};

struct Info{
	Info(int arg_node_id,ll arg_node_size){
		node_id = arg_node_id;
		node_size = arg_node_size;
	}
	bool operator<(const struct Info &arg) const{

		if(node_size != arg.node_size){

			return node_size < arg.node_size;
		}else{

			return node_id < arg.node_id;
		}
	}

	int node_id;
	ll node_size;
};

int N;
int boss[SIZE],height[SIZE],num_member[SIZE];
int count_table[SIZE];
Polygon POLYGON[SIZE];
vector<int> G[SIZE],member[SIZE];
Type color[SIZE],work[SIZE];
bool clear_FLG,visited[SIZE];

int get_boss(int id){
	if(boss[id] == id)return id;
	else{
		return boss[id] = get_boss(boss[id]);
	}
}

int isSame(int x,int y){
	return get_boss(x) == get_boss(y);
}

void unite(int x,int y){
	int boss_x = get_boss(x);
	int boss_y = get_boss(y);

	if(boss_x == boss_y)return;

	if(height[x] > height[y]){

		num_member[boss_x] += num_member[boss_y];
		boss[boss_y] = boss_x;

	}else if(height[x] < height[y]){

		num_member[boss_y] += num_member[boss_x];
		boss[boss_x] = boss_y;

	}else{ //height[x] == height[y]

		num_member[boss_x] += num_member[boss_y];
		boss[boss_y] = boss_x;
		height[x]++;
	}
}

void init(){

	for(int i = 0; i < SIZE; i++){

		POLYGON[i].clear();
		member[i].clear();
		G[i].clear();

		boss[i] = i;
		height[i] = 0;
		num_member[i] = 1;
	}
}


bool is_OK(int group_id){

	for(int i = 0; i < member[group_id].size(); i++){

		visited[member[group_id][i]] = false; //★★塗り残し領域が連結しているとは限らない★★
	}

	for(int i = 0; i < member[group_id].size(); i++){

		int start = member[group_id][i];
		if(work[start] == C || visited[start] == true)continue;

		work[start] = A;
		visited[start] = true;
		queue<int> Q;

		vector<int> TMP;

		Q.push(start);
		TMP.push_back(start);

		while(!Q.empty()){

			for(int k = 0; k < G[Q.front()].size(); k++){

				int next = G[Q.front()][k];
				if(work[next] != UNDEFINED || visited[next] == true){

					continue;
				}

				if(work[Q.front()] == A){

					work[next] = B;

				}else if(work[Q.front()] == B){

					work[next] = A;

				}else{

					return false;
				}
				visited[next] = true;
				Q.push(next);
				TMP.push_back(next);
			}
			Q.pop();
		}

		for(int k = 0; k < TMP.size(); k++){

			if(work[TMP[k]] == UNDEFINED){

				return false; //塗り残しあり
			}

			int from = TMP[k];

			for(int p = 0; p < G[from].size(); p++){
				if(work[from] == work[G[from][p]]){
					return false; //同色隣接
				}
			}
		}
	}

	return true;
}

void recursive(int group_id,int index){

	if(clear_FLG)return;

	if(index == num_member[group_id]){

		for(int i = 0; i < member[group_id].size(); i++){

			work[member[group_id][i]] = color[member[group_id][i]];
		}

		for(int i = 0; i < member[group_id].size(); i++){

			if(color[member[group_id][i]] == UNDEFINED){

				continue;
			}

			int from = member[group_id][i];

			for(int k = 0; k < G[from].size(); k++){
				if(color[from] == color[G[from][k]]){
					return; //C隣接
				}
			}
		}

		//残りの領域を2色で塗れるか判定
		if(is_OK(group_id)){

			clear_FLG = true;
		}
		return;
	}

	int node_id = member[group_id][index];

	if(color[node_id] == C || count_table[node_id] != 0){

		recursive(group_id,index+1);
		return;
	}

	color[node_id] = C;

	for(int i = 0; i < G[node_id].size(); i++){

		count_table[G[node_id][i]]++;
	}
	recursive(group_id,index+1);

	if(clear_FLG)return;

	color[node_id] = UNDEFINED;

	for(int i = 0; i < G[node_id].size(); i++){

		count_table[G[node_id][i]]--;
	}

	for(int i = 0; i < G[node_id].size(); i++){

		int next = G[node_id][i];

		if(count_table[next] == 0 && color[next] != C){

			color[next] = C;
			for(int k = 0; k < G[next].size(); k++){

				count_table[G[next][k]]++;
			}

			recursive(group_id,index+1);

			color[next] = UNDEFINED;
			for(int k = 0; k < G[next].size(); k++){

				count_table[G[next][k]]--;
			}
		}
	}
}

bool can_three(int group_id){

	vector<Info> info;
	for(int i = 0; i < member[group_id].size(); i++){

		info.push_back(Info(member[group_id][i],G[member[group_id][i]].size()));
	}

	sort(info.begin(),info.end());

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

		member[group_id][i] = info[i].node_id;
	}

	for(int i = 0; i < member[group_id].size(); i++){

		color[member[group_id][i]] = UNDEFINED;
		count_table[member[group_id][i]] = 0;

	}

	clear_FLG = false;
	recursive(group_id,0);

	if(clear_FLG){

		return true;
	}else{

		return false;
	}
}

bool can_two(int group_id){

	color[member[group_id][0]] = A;
	for(int i = 1; i < member[group_id].size(); i++){

		color[member[group_id][i]] = UNDEFINED;
	}

	queue<int> Q;
	Q.push(member[group_id][0]);

	while(!Q.empty()){

		for(int i = 0; i < G[Q.front()].size(); i++){

			int next = G[Q.front()][i];

			if(color[next] != UNDEFINED){

				continue;
			}

			if(color[Q.front()] == A){

				color[next] = B;

			}else{

				color[next] = A;
			}
			Q.push(next);
		}

		Q.pop();
	}

	for(int i = 0; i < member[group_id].size(); i++){

		if(color[member[group_id][i]] == UNDEFINED){

			return false;
		}
	}

	for(int i = 0; i < member[group_id].size(); i++){

		int from = member[group_id][i];

		for(int k = 0; k < G[from].size(); k++){
			if(color[from] == color[G[from][k]]){

				return false;
			}
		}
	}

	return true;
}

Point calc_minus(Point a,Point b){
    Point ret;

    ret.x = a.x-b.x;
    ret.y = a.y-b.y;

    return ret;
}

double norm(Vector a){
	return a.x*a.x+a.y*a.y;
}

double abs(Vector a){
	return sqrt(norm(a));
}

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

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

double calc_len(Vector a){
    return sqrt(a.x*a.x+a.y*a.y);
}

//線分ではなく直線と点の距離
double getDistanceLP(Line l,Point p){
    return fabs(cross(calc_minus(l.p[1],l.p[0]),calc_minus(p,l.p[0]))/calc_len(calc_minus(l.p[1],l.p[0])));
}

//点と線分の距離
double getDistanceSP(Line l,Point p){
	if(dot(calc_minus(l.p[1],l.p[0]),calc_minus(p,l.p[0])) < 0.0)return calc_len(calc_minus(p,l.p[0]));
	if(dot(calc_minus(l.p[0],l.p[1]),calc_minus(p,l.p[1])) < 0.0)return calc_len(calc_minus(p,l.p[1]));
	return getDistanceLP(l,p);
}

double calc_slope(Line A){

	if(A.p[0].x > A.p[1].x){

		swap(A.p[0],A.p[1]);
	}

	if(fabs(A.p[0].x-A.p[1].x) < EPS){

		return DBL_MAX;

	}else if(fabs(A.p[0].y-A.p[1].y) < EPS){

		return 0;

	}else{

		return (A.p[0].y-A.p[1].y)/(A.p[0].x-A.p[1].x);
	}
}

//2つの線分に、EPS以上の重なりがあるか調べる関数
bool is_Overwrap(Line A,Line B){

	if(fabs(calc_slope(A)-calc_slope(B)) >= EPS){

		return false;
	}

	if(getDistanceSP(B,A.p[0]) < EPS || getDistanceSP(B,A.p[1]) < EPS){ //線分Aのどちらかの端点がB上にある場合

		if(getDistanceSP(B,A.p[0]) < EPS && getDistanceSP(B,A.p[1]) < EPS){ //両端がB上

			return true;

		}else{

			Point cross_point;

			if(getDistanceSP(B,A.p[0]) < EPS){

				cross_point = A.p[0];

			}else{

				cross_point = A.p[1];
			}

			if(cross_point == B.p[0] || cross_point == B.p[1]){

				//Do nothing

			}else{

				return true;
			}
		}
	}

	if(getDistanceSP(A,B.p[0]) < EPS || getDistanceSP(A,B.p[1]) < EPS){

		if(getDistanceSP(A,B.p[0]) < EPS && getDistanceSP(A,B.p[1]) < EPS){ //両端がA上

			return true;

		}else{

			Point cross_point;

			if(getDistanceSP(A,B.p[0]) < EPS){

				cross_point = B.p[0];

			}else{

				cross_point = B.p[1];
			}

			if(cross_point == A.p[0] || cross_point == A.p[1]){

				//Do nothing

			}else{

				return true;
			}
		}

	}

	return false;
}


void func(){

	init();

	int num_points;
	double X,Y;

	for(int i = 0; i < N; i++){

		scanf("%d",&num_points);

		for(int loop = 0; loop < num_points; loop++){

			scanf("%lf %lf",&X,&Y);

			POLYGON[i].push_back(Point(X,Y));
		}
	}

	//グラフ作成
	for(int i = 0; i < N-1; i++){
		for(int k = i+1; k < N; k++){
			bool FLG = false;
			for(int a = 0; a < POLYGON[i].size(); a++){

				Line tmp_line = Line(POLYGON[i][a],POLYGON[i][(a+1)%POLYGON[i].size()]);

				for(int b = 0; b < POLYGON[k].size(); b++){

					Line adj_line = Line(POLYGON[k][b],POLYGON[k][(b+1)%POLYGON[k].size()]);

					if(is_Overwrap(tmp_line,adj_line)){

						G[i].push_back(k);
						G[k].push_back(i);

						unite(i,k);

						FLG = true;
						break;
					}

				}
				if(FLG)break;
			}
		}
	}

	//国を連結成分ごとに分ける
	for(int i = 0; i < N; i++){

		member[get_boss(i)].push_back(i);
	}

	int maximum = -1;

	for(int i = 0; i < N; i++){

		if(i != get_boss(i))continue;

		if(num_member[i] == 1){

			maximum = max(maximum,1);

		}else if(can_two(i)){

			maximum = max(maximum,2);

		}else if(can_three(i)){

			maximum = max(maximum,3);

		}else{

			maximum = 4;
			break;
		}
	}

	printf("%d\n",maximum);
}

int main(){

	while(true){

		scanf("%d",&N);
		if(N == 0)break;

		func();
	}

	return 0;
}
accuracy: 1
time_ms: 1010
memory_kb: 3276
final_rank: 4

code_snippet: #include<stdio.h>
#include<math.h>
#include<vector>
#include<algorithm>
#include<queue>
using namespace std;
const double EPS = 1e-7;
const double INF = 1e+10;
const double PI = acos(-1);
int sig(double r) { return (r < -EPS) ? -1 : (r > +EPS) ? +1 : 0; }
inline double ABS(double a){return max(a,-a);}
struct Pt {
    double x, y;
    Pt() {}
    Pt(double x, double y) : x(x), y(y) {}
    Pt operator+(const Pt &a) const { return Pt(x + a.x, y + a.y); }
    Pt operator-(const Pt &a) const { return Pt(x - a.x, y - a.y); }
    Pt operator*(const Pt &a) const { return Pt(x * a.x - y * a.y, x * a.y + y * a.x); }
    Pt operator-() const { return Pt(-x, -y); }
    Pt operator*(const double &k) const { return Pt(x * k, y * k); }
    Pt operator/(const double &k) const { return Pt(x / k, y / k); }
    bool operator==(const Pt &a)const{ return !sig(x-a.x)&&!sig(y-a.y);}
    double ABS() const { return sqrt(x * x + y * y); }
    double abs2() const { return x * x + y * y; }
    double arg() const { return atan2(y, x); }
    double dot(const Pt &a) const { return x * a.x + y * a.y; }
    double det(const Pt &a) const { return x * a.y - y * a.x; }
};
double tri(const Pt &a, const Pt &b, const Pt &c) { return (b - a).det(c - a); }
int iSP(Pt a, Pt b, Pt c) {
    int s = sig((b - a).det(c - a));
    if (s) return s;
    if (sig((b - a).dot(c - a)) < 0) return -2; // c-a-b
    if (sig((a - b).dot(c - b)) < 0) return +2; // a-b-c
    return 0;
}
int iLL(Pt a, Pt b, Pt c, Pt d) {
    if (sig((b - a).det(d - c))) return 1; // intersect
    if (sig((b - a).det(c - a))) return 0; // parallel
    return -1; // correspond
}
int sz[40];
int g[40][40];
vector<Pt>p[40];
int deg[40];
int col;
int now[40];
int n;
int nd;
int dfs(int a){
	if(a==nd)return 1;
	int nx=-1;
	int ch=0;
	int re=0;
	int me=0;
	for(int i=0;i<n;i++){
		if(~now[i])continue;
		if(col==3&&deg[i]<3)continue;
		int mask=0;
		int ef=0;
		for(int j=0;j<n;j++){
			if(g[i][j]){
				if(~now[j])mask|=(1<<now[j]);
				else ef++;
			}
		}
		int nch=__builtin_popcount(mask);
		if(nch==col)return 0;
		if(nch>ch||(nch==ch&&me<ef)){
			nx=i;ch=nch;me=ef;re=mask;
		}
	}
	for(int i=0;i<col;i++){
		if(re&(1<<i))continue;
		now[nx]=i;
		if(dfs(a+1))return 1;
	}
	now[nx]=-1;
	return 0;
}
int main(){
	int a;
	while(scanf("%d",&a),a){
		n=a;
		for(int i=0;i<a;i++){
			p[i].clear();
			int b;
			scanf("%d",&b);
			sz[i]=b;
			for(int j=0;j<b;j++){
				int x,y;
				scanf("%d%d",&x,&y);
				p[i].push_back(Pt(x,y));
			}
			p[i].push_back(p[i][0]);
		}
		for(int i=0;i<a;i++)deg[i]=0;
		for(int i=0;i<a;i++)for(int j=0;j<a;j++)g[i][j]=0;
		for(int i=0;i<a;i++)for(int j=i+1;j<a;j++){
			bool ok=false;
			for(int k=0;k<sz[i];k++)for(int l=0;l<sz[j];l++){
				if(iLL(p[i][k],p[i][k+1],p[j][l],p[j][l+1])!=-1)continue;
				if(iSP(p[i][k],p[j][l],p[i][k+1])==2)ok=true;
				if(iSP(p[i][k],p[j][l+1],p[i][k+1])==2)ok=true;
				if(iSP(p[j][l],p[i][k],p[j][l+1])==2)ok=true;
				if(iSP(p[j][l],p[i][k+1],p[j][l+1])==2)ok=true;
				if((p[i][k]-p[j][l]).ABS()<EPS&&(p[i][k+1]-p[j][l+1]).ABS()<EPS)ok=true;
				if((p[i][k]-p[j][l+1]).ABS()<EPS&&(p[i][k+1]-p[j][l]).ABS()<EPS)ok=true;
			}
			if(ok){
				g[i][j]=g[j][i]=1;
				deg[i]++;deg[j]++;
			}
		}
		int ans=4;
		for(int i=1;i<4;i++){
			col=i;
			for(int j=0;j<a;j++)now[j]=-1;
			nd=n;
			if(i==3){
				for(int j=0;j<a;j++)if(deg[j]<3)nd--;
			}
			if(dfs(0)){
				ans=i;break;
			}
		}
		printf("%d\n",ans);
	}
}
accuracy: 1
time_ms: 20
memory_kb: 1060
final_rank: 1

code_snippet: #include <bits/stdc++.h>
#define rep(i,n) for(int i=0;i<(int)(n);i++)
#define rep1(i,n) for(int i=1;i<=(int)(n);i++)
#define all(c) c.begin(),c.end()
#define pb push_back
#define fs first
#define sc second
#define show(x) cout << #x << " = " << x << endl
#define chmin(x,y) x=min(x,y)
#define chmax(x,y) x=max(x,y)
using namespace std;
typedef long long ll;
int N,V;
const int MAX_V=35;
vector<int> G[MAX_V];
bool used[MAX_V];
int ord[MAX_V];
int cnt[MAX_V];		//cnt[v] = the num of selected verticies connected with v;
typedef long long ll;
set<ll> done;

bool can1(){
	rep(i,N) if(!G[i].empty()) return 0;
	return 1;
}
int vis[MAX_V];
bool dfs2(int v,bool t){
	vis[v]=t;
	for(int u:G[v]) if(!used[u]){
		if(vis[u]<0){
			if(!dfs2(u,!t)) return 0;
		}else{
			if(t==vis[u]) return 0;
		}
	}
	return 1;
}
bool can2(){
	rep(i,N) vis[i]=-1;
	rep(i,N) if(!used[i]&&vis[i]<0){
		if(!dfs2(i,0)) return 0;
	}
	return 1;
}
bool c3;
void dfs(int it){
	if(it==V){
		ll x=0;
		rep(i,V) if(used[i]) x|=(1LL<<i);
		if(done.find(x)!=done.end()) return;
		done.insert(x);
//		rep(i,V) cout<<used[i];
//		puts("");
		if(can2()) c3=1;
		return;
	}
	int v=ord[it];
	if(used[v]||cnt[v]){
		dfs(it+1);
		return;
	}
	//use v
	used[v]=1;
	for(int u:G[v]) cnt[u]++;
	dfs(it+1);
	used[v]=0;
	for(int u:G[v]) cnt[u]--;

	for(int u:G[v]) if(cnt[u]==0&&!used[u]){
		used[u]=1;
		for(int w:G[u]) cnt[w]++;
		dfs(it+1);
		used[u]=0;
		for(int w:G[u]) cnt[w]--;
	}
}
void enum_ind(int N){
	V=N;
	typedef pair<int,int> P;
	vector<P> tmp;
	rep(i,N) tmp.pb(P(G[i].size(),i));
	sort(all(tmp));
	rep(i,N) ord[i]=tmp[i].sc;
	rep(i,N) used[i]=0,cnt[i]=0;
	done.clear();
	dfs(0);
}

bool can3(){
	c3=0;
	enum_ind(N);
	return c3;
}


typedef double D;
typedef complex<D> P;
typedef pair<P,P> L;
typedef vector<P> Pol;
D eps=1e-8;
bool eq(D a,D b){return abs(a-b)<eps;}
int sig(D x){return eq(x,0)?0:(x>0?1:-1);}
D cro(P a,P b){return imag(conj(a)*b);}
bool ispal(L a,L b){
	return sig(cro(a.fs-a.sc,b.fs-b.sc))==0;
}
bool hasLen(L a,L b){
	if(!ispal(a,b)) return 0;
	if(sig(cro(a.sc-a.fs,b.fs-a.fs))!=0) return 0;
	P p=(a.sc-a.fs)*P(0,1);
	D x=cro(a.fs-a.fs,p),y=cro(a.sc-a.fs,p),z=cro(b.fs-a.fs,p),w=cro(b.sc-a.fs,p);
	if(x>y) swap(x,y);
	if(z>w) swap(z,w);
	return z+eps<y&&x+eps<w;
}
Pol pol[MAX_V];
int main(){
	while(true){
		cin>>N;
		if(N==0) break;
		rep(i,N) pol[i].clear();
		rep(i,N) G[i].clear();
		rep(i,N){
			int s;
			cin>>s;
			rep(j,s){
				int x,y;
				cin>>x>>y;
				pol[i].pb(P(x,y));
			}
		}
		rep(i,N) rep(j,i){
			bool is=0;
			rep(a,pol[i].size()) rep(b,pol[j].size()){
				L x=L(pol[i][a],pol[i][(a+1)%pol[i].size()]);
				L y=L(pol[j][b],pol[j][(b+1)%pol[j].size()]);
				if(hasLen(x,y)) is=1;
			}
			if(is) G[i].pb(j),G[j].pb(i);
		}
/*		rep(i,N){
			cout<<i<<"   ";
			for(int v:G[i]) cout<<v<<" ";
			puts("");
		}*/
		if(can1()) puts("1");
		else if(can2()) puts("2");
		else if(can3()) puts("3");
		else puts("4");
	}
}
accuracy: 1
time_ms: 620
memory_kb: 17796
final_rank: 6

code_snippet: #include <ctime>
#include <string> 
#include <algorithm> 
#include <cfloat> 
#include <climits> 
#include <cmath> 
#include <complex> 
#include <cstdio> 
#include <cstdlib> 
#include <cstring> 
#include <functional> 
#include <iostream> 
#include <map> 
#include <memory> 
#include <queue> 
#include <set> 
#include <sstream> 
#include <stack> 
#include <utility> 
#include <vector> 

#define EACH(i,c) for(__typeof((c).begin()) i=(c).begin(); i!=(c).end(); ++i)
#define ALL(x) (x).begin(),(x).end() 
typedef long long ll;
using namespace std; 
const double eps = 1e-8;

typedef complex<double> P;
namespace std{
    bool operator<(const P &a, const P &b){
        return real(a) != real(b) ? real(a) < real(b): imag(a) < imag(b);
    }
};
inline double dot(const P &a, const P &b){
    return real(conj(a) * b);
}
inline double cross(const P &a, const P &b){
    return imag(conj(a) * b);
}
int ccw(const P &a, P b, P c){
    b -= a; c -= a;
    if (cross(b, c) > eps)  return +1;  // ccw
    if (cross(b, c) < -eps) return -1;  // cw
    if (dot(b,c) < eps)       return +2;  // c-a-b
    if (norm(b) < norm(c)+eps)  return -2;  // a-b-c
    return 0;                           // a-c-b
}
struct Line : public vector<P> {
    Line() { }
    Line(const P &a, const P &b) {
        push_back(a); push_back(b);
    }
};
inline bool parallel(const Line &s, const Line &t){
    return abs(cross(s[1]-s[0], t[1]-t[0])) < eps;
}
inline bool orthogonal(const Line &s, const Line &t){
    return abs(dot(s[1]-s[0], t[1]-t[0])) < eps;
}
P projection(const Line &s, const P &a){
    P v = s[1] - s[0];
    return s[0] + dot(v, a - s[0]) / abs(v) / abs(v) * v;
}
P reflection(const Line &s, const P &a){
    P p = projection(s, a);
    return 2.0 * p - a;
}
struct Segment : public Line {
    Segment(const P &a, const P &b) : Line(a,b) { }
};
struct Circle{
    P o;
    double r;
    Circle() { }
    Circle(const P &o, double r) : o(o), r(r) { }
    Circle(double x, double y, double r) : o(x,y), r(r) { }
    inline bool contains(const P &p) const{
        return abs(p-o) < r + eps;
    }
    inline bool inside(const P &p) const{
        return abs(p-o) < r - eps;
    }
    pair<Line,Line> tangent(const P &p) const{
        vector<Line> res;
        double d = abs(p - o), s = r*r / d, t = sqrt(r*r-s*s);
        P q = o + s/d * (p - o), e = (p - o) * P(0, 1/d); 
        return make_pair(Line(p,q+e*t), Line(p,q-e*t));
    }
};
typedef vector<P> Polygon;
#define curr(p,i) p[i]
#define next(p,i) p[(i+1)%p.size()]

void print(const P &p){
    cout << "(" << p.real() << "," << p.imag() << ")";
}
bool check(const Polygon &a, const Polygon &b){
    int n = a.size(), m = b.size();
    for(int i = 0; i < n; ++i){
        Segment s1(a[i], next(a,i));
        for(int j=0;j<m;++j){
            Segment s2(b[j], next(b,j));
            if(abs(ccw(s1[0],s1[1],s2[0])) != 1 && abs(ccw(s1[0],s1[1],s2[1])) != 1
                    && (   ccw(s1[0],s1[1],s2[0]) == 0
                        || ccw(s1[0],s1[1],s2[1]) == 0
                        || ccw(s2[0],s2[1],s1[0]) == 0
                        || ccw(s2[0],s2[1],s1[1]) == 0
                        || abs(s2[0]-s1[0]) < eps && abs(s2[1]-s1[1]) < eps
                        || abs(s2[1]-s1[0]) < eps && abs(s2[0]-s1[1]) < eps))
            {
                // print(s1[0]);print(s1[1]);print(s2[0]); print(s2[1]);
                // cout << endl;
                return true;
            }
        }
    }
    return false;
}
bool fnc(){
    Segment s1(P(424,200),P(424,235)), s2(P(424,215),P(424,235));
            if(abs(ccw(s1[0],s1[1],s2[0])) != 1 && abs(ccw(s1[0],s1[1],s2[1])) != 1
                    && (   ccw(s1[0],s1[1],s2[0]) == 0
                        || ccw(s1[0],s1[1],s2[1]) == 0
                        || ccw(s2[0],s2[1],s1[0]) == 0
                        || ccw(s2[0],s2[1],s1[1]) == 0
                        || abs(s2[0]-s1[0]) < eps && abs(s2[1]-s1[1]) < eps
                        || abs(s2[1]-s1[0]) < eps && abs(s2[0]-s1[1]) < eps))
            {
                // print(s1[0]);print(s1[1]);print(s2[0]); print(s2[1]);
                // cout << endl;
                return true;
            }
            return false;
}

struct L{
    int v, neg;
    L(int v, int neg) : v(v),neg(neg) { }
    L(){}
};
bool rndSAT(const vector<vector<L> > &cnf, int n, int k){
    int K = (int)ceil(pow(2*(1-1.0/k), n));
    vector<int> b(n);
    vector<int> ns(cnf.size());
    for(;K>0;--K){
        for(int i=0;i<n;++i) b[i] = rand() & 1;
        for(int t=0;t<3*n;++t){
            int s=0;
            for(int i=0;i<cnf.size();++i){
                bool f = false;
                for(int j=0;j<cnf[i].size();++j)
                    f = f || (b[cnf[i][j].v] ^ cnf[i][j].neg);
                if(!f) ns[s++] = i;
            }
            if(s == 0) return true;
            int x = rand() % s;
            int y = rand() % cnf[x].size();
            b[cnf[x][y].v] ^= 1;
        }
        break;
    }
    return false;
}

typedef int Weight;
struct Edge{
    int from, to;
    Weight weight;
    int rev;     // ネットワークフロー時の逆辺
    Edge(int from, int to, Weight weight) :
        from(from), to(to), weight(weight) { }
    Edge(int from, int to, Weight weight, int rev) :
        from(from), to(to), weight(weight), rev(rev){ }
};
bool operator < (const Edge &a, const Edge &b){
    if(a.weight != b.weight) return a.weight > b.weight;
    if(a.from != b.from) return a.from > b.from;
    return  a.to > b.to;
}
typedef vector<Edge> Edges;
typedef vector<Edges> Graph;

typedef vector<Weight> Array;
typedef vector<Array> Matrix;

void addFlowEdge(Graph &g, int a, int b, Weight c){
    g[a].push_back(Edge(a, b, c, g[b].size()));
    g[b].push_back(Edge(b, a, 0, g[a].size() - 1));
}
void addUndirectedEdge(Graph &g, int a, int b, Weight c){
    g[a].push_back(Edge(a, b, c, g[b].size()));
    g[b].push_back(Edge(b, a, c, g[a].size() - 1));
}
bool bipartiteDFS(const Graph &g,int v, int c, vector<int> &color){
    color[v] = c;
    EACH(i,g[v]){
        if(color[v] == color[i->to]) return false;
        if(color[i->to] < 0)
            if(!bipartiteDFS(g, i->to, 1-c, color)) return false;
    }
    return true;
}
bool bipartite(const Graph &g, vector<int> color){
    bool res = true;
    for(int i=0;i<g.size();++i)
        if(color[i] < 0) res &= bipartiteDFS(g,i,0,color);
    return res;
}
bool func(int t, vector<pair<int,int> > &vp, const Graph &g, vector<int> &used){
    if(t == g.size()){
        vector<int> color = used;
        return bipartite(g, color);
    }
    int v = vp[t].second;
    bool f = true;
    EACH(i, g[v]){
        f = f && used[i->to] < 0;
    }
    if(used[v] > 0) return func(t+1, vp, g, used);
    if(!f) return func(t+1, vp, g, used);
    used[v] = 2;
    if(func(t+1, vp, g, used)) return true;
    used[v] = -1;
    EACH(i, g[v]) if(used[i->to] < 0) {
        f = true;
        EACH(j, g[i->to]) f = f && used[j->to] < 0;
        if(!f) continue;
        used[i->to] = 2;
        if(func(t+1, vp, g, used)) return true;
        used[i->to] = -1;
    }
    return false;
}
bool func2(int t, Graph &g, vector<int> &used){
    if(t == g.size()) return true;
    vector<bool> hoge(3);
    EACH(i,g[t]) if(i->to<t) hoge[used[i->to]] = true;
    for(int i=0;i<3;++i)if(!hoge[i]){
        used[t] = i;
        if(func2(t+1, g, used)) return true;
    }
    return false;
}

int main(){
    srand(time(NULL));
    for(int _=0;;++_){
        int n;
        cin >> n;
        if(n == 0) return 0;
        vector<Polygon> ps(n);
        for(int i = 0; i < n; ++i){
            int m;
            cin >> m;
            ps[i].resize(m);
            for(int j = 0; j < m; ++j){
                double x, y;
                cin >> x >> y;
                ps[i][j] = P(x*2+100,y*3+100);
            }
        }
        bool ne = true;
        Graph g(n);
        for(int i = 0; i < n; ++i){
            for(int j = i+1; j < n; ++j){
                bool f = check(ps[i], ps[j]);
                if(f) addUndirectedEdge(g,i,j,1);
                ne = ne && !f;
            }
        }
        if(ne){
            cout << 1 << endl;
            continue;
        }
        vector<int> col(n,-1);

        if(bipartite(g, col)){
            cout << 2 << endl;
            continue;
        }
        vector<pair<int,int> > vp;
        for(int i=0;i<n;++i) vp.push_back(make_pair(g[i].size(), i));
        sort(ALL(vp)); reverse(ALL(vp));
        map<int,int> mp;
        EACH(i, vp) mp[i->first]++;
        /*
        EACH(i,mp) cout << i->first << "," << i->second << endl;
        EACH(i, g) if(i->size() == 4 || i->size() == 5){
            cout << i->back().from << ":";
            EACH(j, *i){
                sort(ALL(ps[j->to]));
                cout << "(" << j->to << "," << real(ps[j->to][0]) << ","
                    << imag(ps[j->to][0]) << ")";
            }
            cout << endl;
        }
        if(_ != 9) continue;
        */
        vector<int> used(n,-1);
        //cout << ">" << func2(0, g, used) << endl;
        if(func(0, vp, g, used)) cout << 3 << endl;
        else cout << 4 << endl;
        //if(_ == 9) break;
    }
    //cout << fnc() << endl;
}
accuracy: 1
time_ms: 1360
memory_kb: 1308
final_rank: 5

code_snippet: #include<cstdio>
#include<vector>
#include<complex>
#include<cmath>
#include<queue>
#include<utility>
#include<algorithm>

using namespace std;

typedef complex<double> Point;
typedef complex<double> Vector;
typedef vector<Point> Polygon;

typedef pair<int,int> P;

const double eps=1e-9;

template<class T> bool eq(T a,T b){
	return abs(a-b)<eps;
}

template<class T> int sgn(T a){
	if(eq(a,0.0)) return 0;
	if(a>0) return 1;
	return -1;
}

double crP(Vector a,Vector b){
	return (conj(a)*b).imag();
}

bool onSeg(Point a,Point b,Point c){
	return eq(abs(a-b),abs(a-c)+abs(b-c));
}

bool onLine(Point a,Point b,Point c){
	return onSeg(a,b,c)||onSeg(b,c,a)||onSeg(c,a,b);
}

bool cmp(Point a,Point b){
	if(!eq(a.real(),b.real())){
		return a.real()<b.real();
	}
	return a.imag()<b.imag();
}

bool check(Polygon a,Polygon b){
	for(int i=0;i<a.size();i++) for(int j=0;j<b.size();j++){
		Point a1=a[i],a2=a[(i+1)%a.size()];
		Point b1=b[j],b2=b[(j+1)%b.size()];
		bool flg=onLine(a1,a2,b1)&&onLine(a1,a2,b2);
		if(flg==false) continue;
		if(cmp(a1,a2)==false) swap(a1,a2);
		if(cmp(b1,b2)==false) swap(b1,b2);
		if(eq(a1,b1)&&eq(a2,b2)) return true;
		if(eq(a1,b2)||eq(a2,b1)) continue;
		if(onSeg(a1,a2,b1)||onSeg(a1,a2,b2)||onSeg(b1,b2,a1)||onSeg(b1,b2,a1)) return true;
	}
	return false;
}

vector<Polygon> polys;

void readPoly(){
	int N;
	Polygon poly;
	scanf("%d",&N);
	for(int i=0;i<N;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		poly.push_back(Point(x,y));
	}
	polys.push_back(poly);
}

void output(vector<int> vec){
	for(int i=0;i<vec.size();i++){
		printf("%d ",vec[i]);
	}
	printf("\n");
}

vector<int> G[35];
int N;

void getGraph(){
	for(int i=0;i<N;i++) for(int j=i+1;j<N;j++){
		if(check(polys[i],polys[j])){
			G[i].push_back(j);
			G[j].push_back(i);
//			printf("connect %d %d\n",i,j);
		}
	}
}

bool check1(){
	for(int i=0;i<N;i++) if(G[i].size()>0) return false;
	return true;
}

int color[35];
bool check2(){
	queue<int> que;
	for(int i=0;i<N;i++) color[i]=-1;
	for(int i=0;i<N;i++){
		if(color[i]!=-1) continue;
		que.push(i);
		color[i]=0;
		while(!que.empty()){
			int v=que.front();
			que.pop();
			for(int j=0;j<G[v].size();j++){
				int u=G[v][j];
				if(color[u]==color[v]) return false;
				if(color[u]==-1){
					color[u]=color[v]^1;
					que.push(u);
				}
			}
		}
	}
	return true;
}

vector<vector<int> > ind;
bool used[35];
void dfs(vector<int> &cur){
	vector<P> rem;
	for(int i=0;i<N;i++) if(used[i]==false){
		int cnt=0;
		for(int j=0;j<G[i].size();j++){
			if(used[G[i][j]]==false) cnt++;
		}
		rem.push_back(P(cnt,i));
	}
	if(rem.size()==0){
		ind.push_back(cur);
		return;
	}
	sort(rem.begin(),rem.end());
	int v=rem[0].second;
	vector<int> cands;
	cands.push_back(v);
	for(int i=0;i<G[v].size();i++){
		if(used[G[v][i]]==false) cands.push_back(G[v][i]);
	}
	for(int i=0;i<cands.size();i++){
		int v=cands[i];
		vector<int> flipped;
		flipped.push_back(v);
		used[v]=true;
		for(int j=0;j<G[v].size();j++){
			int u=G[v][j];
			if(used[u]==false){
				used[u]=true;
				flipped.push_back(u);
			}
		}
		cur.push_back(v);
		dfs(cur);
		cur.pop_back();
		for(int j=0;j<flipped.size();j++){
			used[flipped[j]]=false;
		}
	}
}

bool check3(){
	vector<int> vec;
	dfs(vec);
//	for(int i=0;i<ind.size();i++){
//		output(ind[i]);
//	}
	int color[35];
	for(int i=0;i<ind.size();i++){
		for(int j=0;j<N;j++) color[j]=-1;
		for(int j=0;j<ind[i].size();j++) color[ind[i][j]]=2;
		queue<int> que;
		bool ok=true;
		for(int j=0;j<N;j++){
			if(color[j]!=-1) continue;
			color[j]=0;
			que.push(j);
			while(!que.empty()){
				int v=que.front();
				que.pop();
				for(int k=0;k<G[v].size();k++){
					int u=G[v][k];
					if(color[u]==color[v]){
						ok=false;
						goto nxtInd;
					}
					if(color[u]==-1){
						color[u]=color[v]^1;
						que.push(u);
					}
				}
			}
		}
		if(ok) return true;
		nxtInd:;
	}
	return false;
}

void solve(){
	if(check1()){
		printf("1\n");
	}else if(check2()){
		printf("2\n");
	}else if(check3()){
		printf("3\n");
	}else{
		printf("4\n");
	}
}

void init(){
	polys.clear();
	for(int i=0;i<35;i++) G[i].clear();
	ind.clear();
	for(int i=0;i<35;i++) used[i]=false;
}

int main(){
	while(true){
		init();
		scanf("%d",&N);
		if(N==0) break;
//		printf("----\n");
		for(int i=0;i<N;i++) readPoly();
		getGraph();
		solve();
	}
	return 0;
}
accuracy: 1
time_ms: 700
memory_kb: 47952
final_rank: 8

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

typedef vector<int> Node;
typedef vector<Node> Graph;

typedef unsigned long long ULL;

inline int trail0(ULL s) { return (s ? __builtin_ctzll(s) : 64); }


using namespace std;
typedef complex<double> P;
typedef vector<P> Pol;
typedef vector<P> L;
P vec(L l){return l[1] - l[0];}

P next(const Pol& pol, int k) {
    if(k == pol.size() - 1) {
        return pol[0];
    } else {
        return pol[k + 1];
    }
}
const double EPS = 1e-6;
int sign(double x){ return x > EPS ? 1 : x < -EPS ? -1 : 0; }

double dot(P a, P b){return real(conj(a) * b);}
double cross(P a, P b){return imag(conj(a) * b);}
int ccw(P a, P b, P c) {
    b -= a; c -= a;
    if (cross(b, c) > +EPS)   return +1; 
    if (cross(b, c) < -EPS)   return -1; 
    if (sign(dot(b, c)) <= 0)     return +2; 
    if (sign(norm(b) - norm(c)) <= 0) return -2; 
    return 0;                        
}
bool paralell(L l, L m){return sign(cross(vec(l), vec(m))) == 0;}
bool equalLL(L l, L m){return paralell(l, m) && sign(cross(vec(l), m[0] - l[0])) == 0;}

bool iSS(L s, L t) {
    if(!equalLL(s, t)) return false;
    P unit = (s[1] - s[0]) / abs(s[1] - s[0]);
    double a = dot(unit, t[0] - s[0]);
    double b = dot(unit, t[1] - s[0]);
    double c = 0;
    double d = dot(unit, s[1] - s[0]);
    if(a > b) swap(a, b);
    if(c > d) swap(c, d);
    if(sign(b - c) <= 0 || sign(d - a) <= 0) return false;
    return true;
}

int n;
bool d[50][50];
int deg[50];
bool a[50];
bool no[50];

int color[50];
bool bip_dfs(int u, int c) {
    if(color[u] != -1 && color[u] != c) return false;
    if(color[u] == c) return true;
    color[u] = c;
    REP(i, n) if(d[u][i] && !a[i]) {
        bool res = bip_dfs(i, c ^ 1);
        if(!res) return false;
    }
    return true;
}

bool bip() {
    REP(i, n) color[i] = -1;
    REP(i, n) if(color[i] == -1 && !a[i]) if(!bip_dfs(i, 0)) return false;
    return true;
}

bool BronKerbosch(const vector<ULL>& g, ULL cur, ULL allowed, ULL forbidden){
    if (allowed == 0 && forbidden == 0) {
        REP(i, n) a[i] = (cur >> i & 1);
        return bip();
    }
    if (allowed == 0) return false;
    int pivot = trail0(allowed | forbidden);
    ULL z = allowed & ~g[pivot];
    for (int u = trail0(z); u < g.size(); u += trail0(z >> (u + 1)) + 1) {
        if(BronKerbosch(g, cur | (1ULL << u), allowed & g[u], forbidden & g[u])) return true;
        allowed ^= 1ULL << u;
        forbidden |= 1ULL << u;
    }
    return false;
}

bool run(){
    assert(n < 64);
    vector<ULL> g(n);
    REP(i, n) REP(j, n) if(i != j) g[i] |= (1ULL << j);
    REP(i, n) REP(j, n) if(i != j && d[i][j]) g[i] ^= (1ULL << j);
    return BronKerbosch(g, 0, (1ULL << n) - 1, 0);
}

int solve() {
    memset(a, 0, sizeof(a));
    memset(no, 0, sizeof(no));

    bool exist = false;
    REP(i, n) REP(j, n) if(d[i][j]) exist = true;

    if(!exist) {
        return 1;
    } else if(bip()) {
        return 2;
    } else if(run()) {
        return 3;
    } else {
        return 4;
    }
}

void max_1() {
    n = 35;
    REP(i, n) REP(j, n) d[i][j] = false;
    assert(solve() == 1);
}

void max_2() {
    n = 35;
    REP(i, n) REP(j, n) d[i][j] = (i % 2 != j % 2);
    assert(solve() == 2);
}

int color_slow[35];
bool slow(int k) {
    if(k == n) {
        REP(i, n) REP(j, n) if(d[i][j] && color_slow[i] == color_slow[j]) return false;
        return true;
    }
    REP(i, 3) {
        color_slow[k] = i;
        if(slow(k + 1)) return true;
    }
    return false;
}


void test() {
    n = 10;
    double pos = 1.0 * rand() / RAND_MAX;
    REP(i, n) REP(j, i) d[i][j] = d[j][i] = (1.0 * rand() / RAND_MAX > pos);

    int ans = (slow(0) ? 3 : 4);
    int act = solve();
    cout << act << endl;
    if(!(act <= 2 || act == ans)) {
        cout << "actual: " << act << endl;
        cout << "assert: " << ans << endl;
        REP(j, n) REP(i, j) if(d[i][j]) cout << i << " <-> " << j << endl;
        assert(false);
    }
}


int main(){
    while(cin >> n && n > 0) {
        vector<Pol> pols(n);
        REP(i, n) {
            int m;
            cin >> m;
            REP(_, m) {
                double x, y;
                cin >> x >> y;
                pols[i].push_back({x, y});
            }
        }

        REP(i, n) REP(j, n) d[i][j] = false;
        REP(i, n) deg[i] = 0;

        for(int i = 0; i < n; i++) {
            for(int k = 0; k < pols[i].size(); k++) {
                L l = {pols[i][k], next(pols[i], k)};
                for(int j = i + 1; j < n; j++) {
                    for(int h = 0; h < pols[j].size(); h++) {
                        L m = {pols[j][h], next(pols[j], h)};
                        if(iSS(l, m)) {
                            d[i][j] = d[j][i] = true;
                            deg[i]++;
                            deg[j]++;
                            break;
                        }
                    }
                }
            }
        }


        cout << solve() << endl;

    }
    return 0;
}
accuracy: 1
time_ms: 470
memory_kb: 1276
final_rank: 2