Problem:
And Then, How Many Are There?
To Mr. Solitarius, who is a famous solo play game creator, a new idea
occurs like every day.
His new game requires discs of various colors and sizes.
To start with, all the discs are randomly scattered around the
center of a table.
During the play, you can remove a pair of discs of the same color if
neither of them has any discs on top of it.
Note that a disc is not considered to be on top of another when they
are externally tangent to each other.
Figure D-1: Seven discs on the table
For example, in Figure D-1, you can only remove the two black
discs first and then their removal makes it possible to remove the two
white ones.
In contrast, gray ones never become removable.
You are requested to write a computer program that, for given colors,
sizes, and initial placings of discs, calculates the maximum number of discs
that can be removed.
Input
The input consists of multiple datasets, each being in the
following format and representing the state of a game just after all
the discs are scattered.
n
x
1
y
1
r
1
c
1
x
2
y
2
r
2
c
2
...
x
n
y
n
r
n
c
n
The first line consists of a positive integer
n
representing
the number of discs.
The following
n
lines, each containing 4 integers separated by
a single space, represent the colors, sizes, and initial placings of
the
n
discs in the following manner:
(
x
i
,
y
i
),
r
i
, and
c
i
are
the
xy
-coordinates of the center, the radius, and the color index
number, respectively, of the
i
-th disc, and
whenever the
i
-th disc is put on top of the
j
-th
disc,
i
<
j
must be satisfied.
You may assume that every color index number is between 1 and 4,
inclusive, and at most 6 discs in a dataset are of the same
color.
You may also assume that the
x
- and
y
-coordinates of the
center of every disc are between 0 and 100, inclusive, and the radius
between 1 and 100, inclusive.
The end of the input is indicated by a single zero.
Output
For each dataset, print a line containing an integer indicating the
maximum number of discs that can be removed.
Sample Input
4
0 0 50 1
0 0 50 2
100 0 50 1
0 0 100 2
7
12 40 8 1
10 40 10 2
30 40 10 2
10 10 10 1
20 10 9 3
30 10 8 3
40 10 7 3
2
0 0 100 1
100 32 5 1
0
Output for the Sample Input
2
4
0

Submitted solutions:

code_snippet: #include<cstdio>
#include<cstdlib>
#include<cstring>
#include<map>
#include<set>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
#define MP(i,j) make_pair(i,j)
typedef long long int64;
typedef long double real;
map<int,int>F;
int x[55],y[55],r[55],c[55],n,i;
int64 fang(int64 a,int64 b,int64 mo)
{
    int64 ans=1;
    for (;b;b>>=1)
      b&1?ans=ans*a%mo:0,a=a*a%mo;
    return ans;
}
template <class T>void updata(T&a,T b){if(a<b)a=b;}
template <class T>void downdata(T&a,T b){if(a>b)a=b;}
int sqr(int x){return x*x;}
bool check(int c,int v)
{
     for (int i=1;i<c;i++)
       if (v&(1<<i-1))
         if (sqr(x[i]-x[c])+sqr(y[i]-y[c])<sqr(r[i]+r[c]))return 0;
     return 1;
 }
int DP(int x)
{
     if (F[x])return F[x];
     for (int i=1;i<=n;i++)
       if (x&(1<<i-1))
         if (check(i,x))
           for (int j=i+1;j<=n;j++)
             if (c[i]==c[j])
               if (x&(1<<j-1))
                 if (check(j,x))
                   updata(F[x],DP(x^(1<<i-1)^(1<<j-1))+2);
     return F[x];
 }
int main()
{
    while (scanf("%d",&n),n)
    {
        F.clear();
        for (i=1;i<=n;i++)scanf("%d%d%d%d",&x[i],&y[i],&r[i],&c[i]);
        printf("%d\n",DP((1<<n)-1));
    }
    return 0;
}
accuracy: 1
time_ms: 5060
memory_kb: 52604
final_rank: 19

code_snippet: #include<cstdio>
#include<cstdlib>
#include<cstring>
#include<map>
#include<set>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
#define MP(i,j) make_pair(i,j)
typedef long long int64;
typedef long double real;
map<int,int>F;
int x[55],y[55],r[55],c[55],n,i;
int64 fang(int64 a,int64 b,int64 mo)
{
    int64 ans=1;
    for (;b;b>>=1)
      b&1?ans=ans*a%mo:0,a=a*a%mo;
    return ans;
}
template <class T>void updata(T&a,T b){if(a<b)a=b;}
template <class T>void downdata(T&a,T b){if(a>b)a=b;}
int sqr(int x){return x*x;}
bool check(int c,int v)
{
     for (int i=1;i<c;i++)
       if (v&(1<<i-1))
         if (sqr(x[i]-x[c])+sqr(y[i]-y[c])<sqr(r[i]+r[c]))return 0;
     return 1;
 }
int DP(int x)
{
     if (F[x])return F[x];
     for (int i=1;i<=n;i++)
       if (x&(1<<i-1))
         if (check(i,x))
           for (int j=i+1;j<=n;j++)
             if (c[i]==c[j])
               if (x&(1<<j-1))
                 if (check(j,x))
                   updata(F[x],DP(x^(1<<i-1)^(1<<j-1))+2);
     return F[x];
 }
int main()
{
    while (scanf("%d",&n),n)
    {
        F.clear();
        for (i=1;i<=n;i++)scanf("%d%d%d%d",&x[i],&y[i],&r[i],&c[i]);
        printf("%d\n",DP((1<<n)-1));
    }
    return 0;
}
accuracy: 1
time_ms: 5040
memory_kb: 52616
final_rank: 18

code_snippet: #include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#define INIT 100
using namespace std;
struct circle
{
    int x, y, r;
    int c;
}cc[24];
char dp[(1<<24)];
int n;
bool intersect(int i, int j)
{
    int x1, y1, x2, y2;
    int r1, r2;
    x1 = cc[i].x, y1 = cc[i].y, r1 = cc[i].r;
    x2 = cc[j].x, y2 = cc[j].y, r2 = cc[j].r;

    if( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) >=  (r1+r2)*(r1+r2))
    {
        return false;

    }//連心線長>=半徑和：無相交
    else
        return true;

}
bool check(int a, int b, int state)
{
    if( ((1<<a-1)&state) == 0 || ((1<<b-1)&state) == 0 )
    {
        return false; //已經被拿走
    }

    if(cc[a].c != cc[b].c) return false; //顏色不同


    int p[2];
    p[0]=a, p[1]=b;

    for(int k=0; k<2; k++)
    {
        int x =  (1<<p[k]-1)-1;  // n-p[k]+1個0  p[k]-1個1
        if( (x&state) > 0)//state的p[k]位右邊有1 ((p[k]可能被其他盤子蓋住
        {
            for(int i=1; i<=p[k]-1; i++)
            {
                int test = 1 << i-1;
                if( (test&state) && intersect(p[k], i) )
                {
                    return false;
                }
            }
        }
    }

    if(1) return true;
}
char DP(int state)
{
    if(state==0) return 0;
    if(dp[state]!=INIT) return dp[state];

    for(int i=1; i<n; i++)
    {
        for(int j=i+1; j<=n; j++)
        {
            if( check(i,j, state) )
            {
                int mask = (1<<i-1) | (1<<j-1);

                dp[state^mask] = DP(state^mask);
                if(dp[state^mask]+2 > dp[state] || dp[state]==INIT)
                    dp[state] = dp[state^mask]+2;
            }
        }
    }
    return dp[state]==INIT? 0:dp[state];
}

int main()
{

    while(scanf("%d",&n) && n)
    {
        for(int i=1; i<=n; i++)
            scanf("%d%d%d%d",&cc[i].x, &cc[i].y, &cc[i].r, &cc[i].c);

        int state = (1<<n)-1;
        for(int i=0; i<=state; i++)
            dp[i] = INIT;

        dp[state] = DP(state);

        printf("%d\n",dp[state]);


    }

    return 0;
}
accuracy: 1
time_ms: 1600
memory_kb: 19184
final_rank: 6

code_snippet: #include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#define INIT 100
using namespace std;
struct circle
{
    int x, y, r;
    int c;
}cc[24];
char dp[(1<<24)];
int n;
bool intersect(int i, int j)
{
    int x1, y1, x2, y2;
    int r1, r2;
    x1 = cc[i].x, y1 = cc[i].y, r1 = cc[i].r;
    x2 = cc[j].x, y2 = cc[j].y, r2 = cc[j].r;

    if( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) >=  (r1+r2)*(r1+r2))
    {
        return false;

    }//連心線長>=半徑和：無相交
    else
        return true;

}
bool check(int a, int b, int state)
{
    if( ((1<<a-1)&state) == 0 || ((1<<b-1)&state) == 0 )
    {
        return false; //已經被拿走
    }

    if(cc[a].c != cc[b].c) return false; //顏色不同


    int p[2];
    p[0]=a, p[1]=b;

    for(int k=0; k<2; k++)
    {
        int x =  (1<<p[k]-1)-1;  // n-p[k]+1個0  p[k]-1個1
        if( (x&state) > 0)//state的p[k]位右邊有1 ((p[k]可能被其他盤子蓋住
        {
            for(int i=1; i<=p[k]-1; i++)
            {
                int test = 1 << i-1;
                if( (test&state) && intersect(p[k], i) )
                {
                    return false;
                }
            }
        }
    }

    return true;
}
char DP(int state)
{
    if(state==0) return 0;
    if(dp[state]!=INIT) return dp[state];

    for(int i=1; i<n; i++)
    {
        for(int j=i+1; j<=n; j++)
        {
            if( check(i,j, state) )
            {
                int mask = (1<<i-1) | (1<<j-1);

                dp[state^mask] = DP(state^mask);
                if(dp[state^mask]+2 > dp[state] || dp[state]==INIT)
                    dp[state] = dp[state^mask]+2;
            }
        }
    }
    return dp[state]==INIT? 0:dp[state];
}

int main()
{

    while(scanf("%d",&n) && n)
    {
        for(int i=1; i<=n; i++)
            scanf("%d%d%d%d",&cc[i].x, &cc[i].y, &cc[i].r, &cc[i].c);

        int state = (1<<n)-1;
        for(int i=0; i<=state; i++)
            dp[i] = INIT;

        dp[state] = DP(state);

        printf("%d\n",dp[state]);


    }

    return 0;
}
accuracy: 1
time_ms: 1600
memory_kb: 19340
final_rank: 8

code_snippet: #include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#define INIT 100
using namespace std;
struct circle
{
    int x, y, r;
    int c;
}cc[24];
char dp[(1<<24)];
int n;
bool intersect(int i, int j)
{
    int x1, y1, x2, y2;
    int r1, r2;
    x1 = cc[i].x, y1 = cc[i].y, r1 = cc[i].r;
    x2 = cc[j].x, y2 = cc[j].y, r2 = cc[j].r;

    if( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) >=  (r1+r2)*(r1+r2))
    {
        return false;

    }//連心線長>=半徑和：無相交
    else
        return true;

}
bool check(int a, int b, int state)
{
    if( ((1<<a-1)&state) == 0 || ((1<<b-1)&state) == 0 )
    {
        return false; //已經被拿走
    }

    if(cc[a].c != cc[b].c) return false; //顏色不同


    int p[2];
    p[0]=a, p[1]=b;

    for(int k=0; k<2; k++)
    {
        int x =  (1<<p[k]-1)-1;  // n-p[k]+1個0  p[k]-1個1
        if( (x&state) > 0)//state的p[k]位右邊有1 ((p[k]可能被其他盤子蓋住
        {
            for(int i=1; i<=p[k]-1; i++)
            {
                int test = 1 << i-1;
                if( (test&state) && intersect(p[k], i) )
                {
                    return false;
                }
            }
        }
    }

    return true;
}
int DP(int state)
{
    if(state==0) return 0;
    if(dp[state]!=INIT) return dp[state];

    for(int i=1; i<n; i++)
    {
        for(int j=i+1; j<=n; j++)
        {
            if( check(i,j, state) )
            {
                int mask = (1<<i-1) | (1<<j-1);

                dp[state^mask] = DP(state^mask);
                if(dp[state^mask]+2 > dp[state] || dp[state]==INIT)
                    dp[state] = dp[state^mask]+2;
            }
        }
    }
    return dp[state]==INIT? 0:dp[state];
}

int main()
{

    while(scanf("%d",&n) && n)
    {
        for(int i=1; i<=n; i++)
            scanf("%d%d%d%d",&cc[i].x, &cc[i].y, &cc[i].r, &cc[i].c);

        int state = (1<<n)-1;
        for(int i=0; i<=state; i++)
            dp[i] = INIT;

        dp[state] = DP(state);

        printf("%d\n",dp[state]);


    }

    return 0;
}
accuracy: 1
time_ms: 1600
memory_kb: 19188
final_rank: 7

code_snippet: #include<stdio.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
struct circle
{
    double x, y, r;
    int c;
}cc[30];
int dp[(1<<24)];
int n;
bool intersect(int i, int j)
{
    double x1, y1, x2, y2;
    double r1, r2;
    x1 = cc[i].x, y1 = cc[i].y, r1 = cc[i].r;
    x2 = cc[j].x, y2 = cc[j].y, r2 = cc[j].r;

    if( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) >=  (r1+r2)*(r1+r2))
    {
        return false;

    }//連心線長>=半徑和：無相交
    else
        return true;

}
bool check(int a, int b, int state)
{
    if( ((1<<a-1)&state) == 0 || ((1<<b-1)&state) == 0 )
    {
        return false; //已經被拿走
    }

    if(cc[a].c != cc[b].c) return false; //顏色不同


    int p[2];
    p[0]=a, p[1]=b;

    for(int k=0; k<2; k++)
    {
        int x =  (1<<p[k]-1)-1;  // n-p[k]+1個0  p[k]-1個1
        if( (x&state) > 0)//state的p[k]位右邊有1 ((p[k]可能被其他盤子蓋住
        {
            for(int i=1; i<=p[k]-1; i++)
            {
                int test = 1 << i-1;
                if( test&state )
                {
                    if(intersect(p[k], i))
                        return false;
                }
            }
        }
    }
    return true;
}
int DP(int state)
{
    if(state==0) return 0;
    if(dp[state]!=-1) return dp[state];

    for(int i=1; i<n; i++)
    {
        for(int j=i+1; j<=n; j++)
        {
            if( check(i,j, state) )
            {
                int mask = (1<<i-1) | (1<<j-1);

                dp[state^mask] = DP(state^mask);
                if(dp[state^mask]+2 > dp[state])
                    dp[state] = dp[state^mask]+2;
            }
        }
    }
    return dp[state]==-1? 0:dp[state];
}

int main()
{

    while(scanf("%d",&n) && n)
    {
        for(int i=1; i<=n; i++)
            scanf("%lf%lf%lf%d",&cc[i].x, &cc[i].y, &cc[i].r, &cc[i].c);

        int state = (1<<n)-1;
        for(int i=0; i<=state; i++)
            dp[i] = -1;

        dp[state] = DP(state);

        printf("%d\n",dp[state]);


    }

    return 0;
}
accuracy: 1
time_ms: 1920
memory_kb: 68420
final_rank: 15

code_snippet: #include<stdio.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
struct circle
{
    double x, y, r;
    int c;
}cc[30];
int dp[(1<<24)];
int n;
bool intersect(int i, int j)
{
    double x1, y1, x2, y2;
    double r1, r2;
    x1 = cc[i].x, y1 = cc[i].y, r1 = cc[i].r;
    x2 = cc[j].x, y2 = cc[j].y, r2 = cc[j].r;

    if( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) >=  (r1+r2)*(r1+r2))
    {
        return false;

    }//連心線長>=半徑和：無相交
    else
        return true;

}
bool check(int a, int b, int state)
{
    if( ((1<<a-1)&state) == 0 || ((1<<b-1)&state) == 0 )
    {
        return false; //已經被拿走
    }

    if(cc[a].c != cc[b].c) return false; //顏色不同


    int p[2];
    p[0]=a, p[1]=b;

    for(int k=0; k<2; k++)
    {
        int x =  (1<<p[k]-1)-1;  // n-p[k]+1個0  p[k]-1個1
        if( (x&state) > 0)//state的p[k]位右邊有1 ((p[k]可能被其他盤子蓋住
        {
            for(int i=1; i<=p[k]-1; i++)
            {
                int test = 1 << i-1;
                if( test&state )
                {
                    if(intersect(p[k], i))
                    {
                        return false;
                    }

                }
            }
        }
    }
    return true;
}
int DP(int state)
{
    if(state==0) return 0;
    if(dp[state]!=-1) return dp[state];

    for(int i=1; i<n; i++)
    {
        for(int j=i+1; j<=n; j++)
        {
            if( check(i,j, state) )
            {
                int mask = (1<<i-1) | (1<<j-1);

                dp[state^mask] = DP(state^mask);
                if(dp[state^mask]+2 > dp[state])
                    dp[state] = dp[state^mask]+2;
            }
        }
    }
    return dp[state]==-1? 0:dp[state];
}

int main()
{

    while(scanf("%d",&n) && n)
    {
        for(int i=1; i<=n; i++)
            scanf("%lf%lf%lf%d",&cc[i].x, &cc[i].y, &cc[i].r, &cc[i].c);

        int state = (1<<n)-1;
        for(int i=0; i<=state; i++)
            dp[i] = -1;

        dp[state] = DP(state);

        printf("%d\n",dp[state]);


    }

    return 0;
}
accuracy: 1
time_ms: 1920
memory_kb: 68524
final_rank: 17

code_snippet: #include<stdio.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
struct circle
{
    double x, y, r;
    int c;
}cc[30];
int dp[(1<<24)];
int n;
bool intersect(int i, int j)
{
    double x1, y1, x2, y2;
    double r1, r2;
    x1 = cc[i].x, y1 = cc[i].y, r1 = cc[i].r;
    x2 = cc[j].x, y2 = cc[j].y, r2 = cc[j].r;

    if( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) >=  (r1+r2)*(r1+r2))
    {
        return false;

    }//連心線長>=半徑和：無相交
    else
        return true;

}
bool check(int a, int b, int state)
{
    if( ((1<<a-1)&state) == 0 || ((1<<b-1)&state) == 0 )
    {
        return false; //已經被拿走
    }

    if(cc[a].c != cc[b].c) return false; //顏色不同


    int p[2];
    p[0]=a, p[1]=b;

    for(int k=0; k<2; k++)
    {
        int x =  (1<<p[k]-1)-1;  // n-p[k]+1個0  p[k]-1個1
        if( (x&state) > 0)//state的p[k]位右邊有1 ((p[k]可能被其他盤子蓋住
        {
            for(int i=1; i<=p[k]-1; i++)
            {
                int test = 1 << i-1;
                if( (test&state) && intersect(p[k], i) )
                {
                    return false;
                }
            }
        }
    }

    return true;
}
int DP(int state)
{
    if(state==0) return 0;
    if(dp[state]!=-1) return dp[state];

    for(int i=1; i<n; i++)
    {
        for(int j=i+1; j<=n; j++)
        {
            if( check(i,j, state) )
            {
                int mask = (1<<i-1) | (1<<j-1);

                dp[state^mask] = DP(state^mask);
                if(dp[state^mask]+2 > dp[state])
                    dp[state] = dp[state^mask]+2;
            }
        }
    }
    return dp[state]==-1? 0:dp[state];
}

int main()
{

    while(scanf("%d",&n) && n)
    {
        for(int i=1; i<=n; i++)
            scanf("%lf%lf%lf%d",&cc[i].x, &cc[i].y, &cc[i].r, &cc[i].c);

        int state = (1<<n)-1;
        for(int i=0; i<=state; i++)
            dp[i] = -1;

        dp[state] = DP(state);

        printf("%d\n",dp[state]);


    }

    return 0;
}
accuracy: 1
time_ms: 1920
memory_kb: 68456
final_rank: 16

code_snippet: #pragma GCC target("avx2")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")

// #ifndef ONLINE_JUDGE
// #define _GLIBCXX_DEBUG // 配列外参照のエラー
// #endif

// スタンダードライブラリ
#include <bits/stdc++.h>
using namespace std;

// 型関連
using ll = long long;
using lint = long long;
using pll = pair<ll, ll>;
using plll = tuple<ll, ll, ll>;
using pii = pair<int, int>;
using piii = tuple<ll, ll, ll>;
using vi = vector<int>;
using vvi = vector<vector<int>>;
using vvvi = vector<vector<vector<int>>>;
using vvvvi = vector<vector<vector<vector<int>>>>;
using vl = vector<ll>;
using vvl = vector<vector<ll>>;
using vvvl = vector<vector<vector<ll>>>;
using vvvvl = vector<vector<vector<vector<ll>>>>;

template <class T> using prique = priority_queue<T>;
template <class T> void chmin(T &a, T b) {
    if (a > b)
        a = b;
}
template <class T> void chmax(T &a, T b) {
    if (a < b)
        a = b;
}

#define all(v) v.begin(), v.end()

// 型定数
constexpr ll mod = 998244353;
constexpr ll MOD = 1000000007;
int INF32 = 2e9;
ll INF64 = 9e18;
#define endl '\n';

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

/////////////////////////// print関連
template <typename T> void print(vector<T> A);
void print();
template <class Head, class... Tail> void print(Head &&head, Tail &&...tail);
void print(string S);
template <typename T> void print(vector<vector<T>> &F);

inline void Yes(bool f);

///////////////////ここから//////////////////////
bool solve() {
    int N;
    cin >> N;
    if (N == 0) {
        return false;
    }

    vector<pii> XY(N);
    vector<int> R(N), C(N);
    for (int i = 0; i < N; i++) {
        int x, y, c, r;
        cin >> x >> y >> r >> c;
        // print(x, y, c, r);
        XY[i] = {x, y};
        R[i] = r;
        C[i] = c;
        // cin >> XY[i].first >> XY[i].second >> R[i] >> C[i];
    }

    auto overlap = [&](int i, int j) -> bool {
        auto [x1, y1] = XY[i];
        auto [x2, y2] = XY[j];
        int r1 = R[i], r2 = R[j];
        return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) <
               (r1 + r2) * (r1 + r2);
    };

    auto ok = [&](int i, int S) -> bool {
        for (int j = 0; j < i; j++) {
            if ((S & (1 << j)) == 0 && overlap(i, j))
                return false;
        }
        return true;
    };

    int ans = 0;

    vi dp(1 << N);
    dp[0] = 1;
    for (int S = 0; S < (1 << N); S++) {
        if (dp[S] == 0)
            continue;
        int tmp = 0;
        for (int i = 0; i < N; i++) {
            if (S & (1 << i)) {
                tmp++;
                continue;
            }
            if (ok(i, S) == false)
                continue;

            for (int j = i + 1; j < N; j++) {
                if (S & (1 << j))
                    continue;
                if (C[i] == C[j] && ok(j, S)) {
                    int nS = S + (1 << i) + (1 << j);
                    dp[nS] = 1;
                }
            }
        }
        chmax(ans, tmp);
    }
    print(ans);

    return true;
}

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

////////////////////print/////////////////////////
// 1次元ベクトルを出力する
template <typename T> void print(vector<T> A) {
    if (A.size() == 0) {
        return;
    }
    for (size_t i = 0; i < A.size() - 1; i++) {
        cout << A[i] << ' ';
    }
    cout << A[A.size() - 1] << endl;
    return;
}

// 2次元ベクトルを出力する
template <typename T> void print(vector<vector<T>> &F) {
    for (size_t i = 0; i < F.size(); i++) {
        for (size_t j = 0; j < F[i].size(); j++) {
            cout << F[i][j];
            if (j < F[i].size() - 1) {
                cout << ' ';
            }
        }
        cout << endl;
    }
}

// 空白行を出力するprint
void print() { cout << endl; }

// 数値・文字列を空白区切りで出力する. print(a,b)など
template <class Head, class... Tail> void print(Head &&head, Tail &&...tail) {
    cout << head;
    if (sizeof...(tail) != 0)
        cout << " ";
    print(forward<Tail>(tail)...);
}

void print(string S) { cout << S << endl; }

//////////////出力関連

inline void Yes(bool f) {
    if (f) {
        cout << "Yes" << endl;
    } else {
        cout << "No" << endl;
    }
}
accuracy: 1
time_ms: 1800
memory_kb: 68984
final_rank: 14

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

const long long MOD1 = 1000000007;
const long long MOD2 = 998244353;
#define all(v) v.begin(), v.end()
#define rall(a) a.rbegin(), a.rend()
#define fi first
#define se second
#define endl "\n"

#define chmax(x, y) x = max(x, y)
#define chmin(x, y) x = min(x, y)

template <typename T>
using vc = vector<T>; // prioriy_queueに必要なのでここにこれ書いてます
template <typename T>
using vv = vc<vc<T>>;
#define int long long
using ll = long long;
ll INF = 2e18;

#ifdef ONLINE_JUDGE
// ジャッジ環境ではデバッグマクロを無効化
#define debug_x(x) \
    do             \
    {              \
    } while (0)
#define debug_vc(v) \
    do              \
    {               \
    } while (0)
#define debug_vv(v) \
    do              \
    {               \
    } while (0)
#else
// ローカルではデバッグ出力
#define debug_x(x) cout << #x << " = " << (x) << endl
#define debug_vc(v)                    \
    {                                  \
        cout << #v << endl;            \
        ll n = size(v);                \
        rep(i, n) cout << v[i] << ' '; \
        cout << endl;                  \
    } // 一次元配列を出力する
#define debug_vv(v)                                        \
    {                                                      \
        cout << #v << endl;                                \
        ll n = size(v);                                    \
        rep(i, n)                                          \
        {                                                  \
            rep(j, size(v[i])) { cout << v[i][j] << ' '; } \
            cout << endl;                                  \
        }                                                  \
    } // 二次元配列を出力する
#endif

#define rep(i, n) for (ll i = 0; i < (n); ++i)
#define rrep(i, n) for (ll i = 1; i <= (n); ++i)
#define drep(i, n) for (ll i = (n) - 1; i >= 0; --i)
#define nfor(i, s, n) for (ll i = s; i < n; i++)        // i=s,s+1...n-1 ノーマルfor
#define dfor(i, s, n) for (ll i = (s) - 1; i >= n; i--) // s-1スタートでnまで落ちる

using ld = long double;
using bl = bool;

template <class T>
using pq = priority_queue<T, vc<T>>; // 大きい順
template <class T>
using pq_g = priority_queue<T, vc<T>, greater<T>>; // 小さい順

using vl = vc<ll>;
using vvl = vv<ll>;
using vvvl = vv<vl>;
using vvvvl = vv<vvl>;
using vs = vc<string>;
using vvs = vv<string>;
using vb = vc<bl>;
using vvb = vv<bl>;
using vvvb = vv<vb>;
using vld = vc<ld>;
using vvld = vv<ld>;
using vvvld = vv<vld>;
using P = pair<ll, ll>;
using vP = vc<P>;
using vvP = vc<vP>;
using vvvP = vv<vP>;

#define pb push_back

#define YES cout << "Yes" << endl
#define NO cout << "No" << endl
#define YN                     \
    {                          \
        cout << "Yes" << endl; \
    }                          \
    else                       \
    {                          \
        cout << "No" << endl;  \
    } // if(a==b)YN;
#define dame cout << -1 << endl

string dir = "DRUL";   // 第4象限の場合
vl di = {1, 0, -1, 0}; // vl di={1,1,0,-1,-1,-1,0,1};
vl dj = {0, 1, 0, -1}; // vl dj={0,1,1,1,0,-1,-1,-1};

bool out_grid(ll i, ll j, ll h, ll w)
{ // trueならcontinue
    return (!(0 <= i && i < h && 0 <= j && j < w));
}

ll n;
vl x, y, r, c;
vb visited;

ll toState(vb &eliminated)
{
    ll idx = 0;
    rep(i, n)
    {
        if (eliminated[i])
        {
            idx += 1 << i;
        }
    }
    return idx;
}

ll dfs(vb &eliminated)
{
    // debug_vc(eliminated);
    ll ans = 0;
    vl board;
    rep(i, n)
    {
        if (eliminated[i])
            continue;
        bl ok = true;
        rep(j, i)
        {
            if (eliminated[j])
                continue;
            // ll j = board[ii];
            ll d = (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]);
            if ((r[i] + r[j]) * (r[i] + r[j]) > d)
            {
                ok = false;
                break;
            }
        }
        if (ok)
        {
            board.pb(i);
        }
    }

    rep(i, board.size())
    {
        rep(j, board.size())
        {
            if (i >= j)
                continue;
            if (c[board[i]] == c[board[j]])
            {
                eliminated[board[i]] = true;
                eliminated[board[j]] = true;
                ll state = toState(eliminated);
                if (!visited[state])
                {
                    visited[state] = true;
                    ans = max(ans, 2 + dfs(eliminated));
                }
                eliminated[board[i]] = false;
                eliminated[board[j]] = false;
            }
        }
    }

    // debug(board);
    // vvl colSet(4);
    // rep(i, board.size())
    // {
    //     colSet[c[board[i]] - 1].pb(board[i]);
    //     // rep(j, board.size())
    //     // {
    //     //     if (i >= j)
    //     //         continue;
    //     //     if (c[board[i]] == c[board[j]])
    //     //     {
    //     //         eliminated[board[i]] = true;
    //     //         eliminated[board[j]] = true;
    //     //         ans = max(ans, 2 + dfs(eliminated));
    //     //         eliminated[board[i]] = false;
    //     //         eliminated[board[j]] = false;
    //     //     }
    //     // }
    // }
    // bl update = false;
    // rep(i, 4)
    // {
    //     if (colSet[i].size() >= 2)
    //     {
    //         update = true;
    //     }
    // }
    // if (!update)
    //     return 0;
    // ll res = 0;
    // rep(i, 4)
    // {
    //     if (colSet[i].size() % 2 == 0)
    //     {
    //         // 全部取り除ける
    //         rep(j, colSet[i].size())
    //         {
    //             eliminated[colSet[i][j]] = true;
    //             res++;
    //         }
    //     }
    // }

    // bl dive = false;
    // rep(i, 4)
    // {
    //     if (colSet[i].size() % 2 == 1)
    //     {
    //         // 1枚余る
    //         rep(j, colSet[i].size())
    //         {
    //             eliminated[colSet[i][j]] = true;
    //             res++;
    //         }
    //         rep(j, colSet[i].size())
    //         {
    //             eliminated[colSet[i][j]] = false;
    //             res--;
    //             dive = true;
    //             ans = max(ans, res + dfs(eliminated));
    //             eliminated[colSet[i][j]] = true;
    //             res++;
    //         }
    //         rep(j, colSet[i].size())
    //         {
    //             eliminated[colSet[i][j]] = false;
    //             res--;
    //         }
    //     }
    // }

    // if (!dive)
    // {
    //     ans = max(ans, res + dfs(eliminated));
    // }

    // rep(i, 4)
    // {
    //     if (colSet[i].size() % 2 == 0)
    //     {
    //         // 全部取り除ける
    //         rep(j, colSet[i].size())
    //         {
    //             eliminated[colSet[i][j]] = false;
    //             res--;
    //         }
    //     }
    // }

    return ans;
}

bl solve()
{
    cin >> n;
    if (n == 0)
        return 0;
    x.resize(n);
    y.resize(n);
    r.resize(n);
    c.resize(n);
    rep(i, n)
    {
        cin >> x[i] >> y[i] >> r[i] >> c[i];
    }
    // 全探索(DFS)
    // 重なっている⇔2r>2頂点間の距離
    // bit全探索
    // rep(i,1<<n){
    //     rep(j,n){
    //         if(i&(1<<j))continue;
    //         rep(k,j){
    //             if(i&(1<<k))continue;
    //         }
    //     }
    // }
    visited.assign(1<<n,false);
    vb eliminated(n);
    cout << dfs(eliminated) << endl;

    return 1;
}

signed main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    while (true)
    {
        if (!solve())
            break;
    }
    return 0;
}

/*
shift+alt+Fで成形
f2で変数名変更

スニペット
basic
 m:atcoder以外用
 ma:ABC/ARC用
 mahc:AHC用
math
 floor:床関数
 ceil:天井関数
 comv:二項係数
 modpow: a^b mod m
 GCD:最大公約数
 extGCD:拡張ユークリッド互除法
 PFD:素因数分解
 isprime:エラトステネスの篩
 matrixpow:行列累乗
data_structure
 seg:セグメント木
technic
 Compress:座標圧縮
 runlength:ランレングス圧縮
graph
 warshall:ワーシャルフロイド法
 dfs:深さ優先探索
 bfs:幅優先探索
 LCA:最近共通祖先
*/
accuracy: 1
time_ms: 1800
memory_kb: 4996
final_rank: 5

code_snippet: #include <bits/stdc++.h>
using namespace std;
typedef int ll;
typedef unsigned long long ull;
typedef long double ld;
#define rep(i, n) for (ll i = 0; i < (ll)(n); i++)
#define rrep(i,start,end) for (ll i = start;i >= (ll)(end);i--)
#define repn(i,end) for(ll i = 0; i <= (ll)(end); i++)
#define reps(i,start,end) for(ll i = start; i < (ll)(end); i++)
#define repsn(i,start,end) for(ll i = start; i <= (ll)(end); i++)
#define each(p,a) for(auto &p:a)
typedef vector<ll> vll;
typedef vector<pair<ll ,ll>> vpll;
typedef vector<vector<ll>> vvll;
typedef set<ll> sll;
typedef map<ll , ll> mpll;
typedef pair<ll ,ll> pll;
typedef tuple<ll , ll , ll> tpl3;
#define LL(...) ll __VA_ARGS__; input(__VA_ARGS__)
#define LD(...) ld __VA_ARGS__; input(__VA_ARGS__)
#define Str(...) string __VA_ARGS__; input(__VA_ARGS__)
#define Ch(...) char __VA_ARGS__; input(__VA_ARGS__)
#define all(a)  (a).begin(),(a).end()
#define UNIQUE(v) v.erase( unique(v.begin(), v.end()), v.end() );
#define sz(x) (ll)x.size()
// << std::fixed << std::setprecision(10)
const ll INF = 1 << 30;
const ld EPS = 1e-9;
 
ll lceil(ll a,ll b){if(a%b==0){return a/b;}if(a>=0){return (a/b)+1;}else{return -((-a)/b);}}
ll lfloor(ll a,ll b){if(a%b==0){return a/b;}if(a>=0){return (a/b);}else{return -((-a)/b)-1;}}
inline ll positive_mod(ll a,ll m){return (a % m + m)%m;}
inline ll popcnt(ull a){ return __builtin_popcountll(a);}
//0indexed
inline ll topbit(ll a){assert(a != 0);return 63 - __builtin_clzll(a);}
inline ll smlbit(ll a){assert(a != 0);return __builtin_ctzll(a);}
template<class T> bool chmin(T& a, T b){if(a > b){a = b;return true;}return false;}
template<class T> bool chmax(T& a, T b){if(a < b){a = b;return true;}return false;}
template<typename T> std::istream &operator>>(std::istream&is,std::vector<T>&v){for(T &in:v){is>>in;}return is;}
template<typename T> std::ostream &operator<<(std::ostream&os,const std::vector<T>&v){for(auto it=std::begin(v);it!=std::end(v);){os<<*it<<((++it)!=std::end(v)?" ":"");}return os;}
template<typename T1, typename T2>std::ostream &operator<< (std::ostream &os, std::pair<T1,T2> p){os << "{" << p.first << "," << p.second << "}";return os;}
template<class... T>void input(T&... a){(cin >> ... >> a);}
void print(){cout << endl;}
template<class T, class... Ts>void print(const T& a, const Ts&... b){cout << a;((cout << ' ' << b), ...);cout << endl;}
template<class T> void pspace(const T& a){ cout << a << ' ';}
void perr(){cerr << endl;}
template<class T, class... Ts>void perr(const T& a, const Ts&... b){cerr << a;((cerr << ' ' << b), ...);cerr << endl;}
void yes(bool i = true){ return print(i?"yes":"no"); }
void Yes(bool i = true){ return print(i?"Yes":"No"); }
void YES(bool i = true){ return print(i?"YES":"NO"); }
template <class T> vector<T> &operator++(vector<T> &v) {for(auto &e : v) e++;return v;}
template <class T> vector<T> operator++(vector<T> &v, signed) {auto res = v;for(auto &e : v) e++;return res;}
template <class T> vector<T> &operator--(vector<T> &v) {for(auto &e : v) e--;return v;}
template <class T> vector<T> operator--(vector<T> &v, signed) {auto res = v;for(auto &e : v) e--;return res;}
//grid探索用
vector<ll> _ta = {0,0,1,-1,1,1,-1,-1};
vector<ll> _yo = {1,-1,0,0,1,-1,1,-1};
bool isin(ll now_i,ll now_j,ll h,ll w){return (0<=now_i && now_i < h && 0 <= now_j && now_j < w);}
  
ll lpow(ll x,ll n){ll ans = 1;while(n >0){if(n & 1)ans *= x;x *= x;n >>= 1;}return ans;}
ll Modlpow(ll x,ll n,ll m){ll ans = 1;ll a = x%m;while(n >0){if(n & 1){ans *= a;ans%= m;}a *= a;a %= m;n >>= 1;}return ans;} 
const ll MOD9 = 998244353LL;
const ll MOD10 = 1000000007LL;

using tpl4 = tuple<ll,ll,ll,ll> ;
void solve(ll n){
  vector<tpl4> xyrc(n);
  rep(i,n){
    LL(x,y,r,c);
    xyrc[i] = {x,y,r,c};
  }

  auto cross=[&](ll i,ll j){
    auto [xi,yi,ri,ci] = xyrc[i];
    auto [xj,yj,rj,cj] = xyrc[j];
    if(lpow(ri+rj,2)>lpow(abs(xi-xj),2)+lpow(abs(yi-yj),2)){
      return true;
    }else return false;
  };
  
  vvll g(n);//上からした
  rep(i,n){
    reps(j,i+1,n){
      if(cross(i,j)){
        g[i].push_back(j);
      }
    }
  }
  queue<ll> que;
  //0からの距離が入る
  //01BFSのときは更新の判定のところを変更する
  vector<ll> find(1 << n,INF);
  find[(1<<n)-1] = 0;
  que.push((1<<n)-1);
  while(!que.empty()){
    ll now = que.front();
    que.pop();
    vll topable(n,1);
    vll onbit;
    rep(i,n){
      if(now >> i &1){
        onbit.push_back(i);
        each(p,g[i]){
          topable[p] = 0;
        }
      }
    }
    each(p,onbit){
      each(q,onbit){
        if(p == q)break;
        if(topable[p]&&topable[q]&&get<3>(xyrc[p])==get<3>(xyrc[q])){
          ll nxt = now ^1<<p ^1<<q;
          if(find[nxt] ==INF){
            find[nxt] = find[now]+2;
            que.push(nxt);
          }
        }
      }
    }
  }
  ll ans =0;
  rep(i,1 <<n)if(find[i] < INF){
    chmax(ans,find[i]);
  }
  cout << ans << endl;



}
 
int main(){
  ios::sync_with_stdio(false);cin.tie(nullptr);
  while(true){
    LL(n);
    if(n == 0)break;
    solve(n);
  }
}
accuracy: 1
time_ms: 600
memory_kb: 70504
final_rank: 12

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

using ll = long long;
using vi = vector<int>;
using vl = vector<long long>;
using vs = vector<string>;
using vc = vector<char>;
using vb = vector<bool>;
using vpii = vector<pair<int, int>>;
using vpll = vector<pair<long long, long long>>;
using vvi = vector<vector<int>>;
using vvl = vector<vector<long long>>;
using vvc = vector<vector<char>>;
using vvvi = vector<vector<vector<int>>>;
using pii = pair<int, int>;
using vvb = vector<vector<bool>>;
using Graph = vector<vector<ll>>;
const ll LINF = 1001002003004005006ll;
const int INF = 1001001001;
const int MOD = 998244353;

#define rep(i, n) for (int i = 0; i < (int)(n); i++)
#define rep3(i, m, n) for (int i = (m); i < (int)(n); i++)
#define rrep(i, m, n) for (int i = (m); i >= (int)(n); i--)
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()

template <typename T1, typename T2> inline bool chmax(T1 &a, T2 b) { return a < b && (a = b, true); }
template <typename T1, typename T2> inline bool chmin(T1 &a, T2 b) { return a > b && (a = b, true); }

vi dx4 = {1, 0, -1, 0}, dy4 = {0, 1, 0, -1};
vi dx8 = {1, 1, 1, 0, 0, -1, -1, -1}, dy8 = {1, 0, -1, 1, -1, 1, 0, -1};

set<set<array<int, 2>>> pair_divide(vector<int> V)
{
    int N = V.size();

    set<set<array<int, 2>>> pairss;

    do
    {
        set<array<int, 2>> pairs;
        for (int i = 0; i + 1 < N; i += 2)
        {
            int a = V[i], b = V[i + 1];
            if (a > b)
                swap(a, b);
            pairs.insert({a, b});
        }
        pairss.insert(pairs);

    } while (next_permutation(V.begin(), V.end()));

    return pairss;
}

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

    vector<int> X(N), Y(N), R(N), C(N);
    vector<vector<int>> color_idx(4);
    for (int i = 0; i < N; i++)
    {
        cin >> X[i] >> Y[i] >> R[i] >> C[i];
        C[i]--;
        color_idx[C[i]].push_back(i);
    }

    vector<int> covered_init(N, 0);
    vector<vector<int>> covering(N);
    for (int i = 0; i < N; i++)
    {
        for (int j = i + 1; j < N; j++)
        {
            // 重なっていれば
            if ((X[i] - X[j]) * (X[i] - X[j]) + (Y[i] - Y[j]) * (Y[i] - Y[j]) < (R[i] + R[j]) * (R[i] + R[j]))
            {
                covered_init[j]++;
                covering[i].push_back(j);
            }
        }
    }

    set<set<array<int, 2>>> C1, C2, C3, C4;
    C1 = pair_divide(color_idx[0]);
    C2 = pair_divide(color_idx[1]);
    C3 = pair_divide(color_idx[2]);
    C4 = pair_divide(color_idx[3]);

    // for (auto a : C1)
    // {
    //     for (auto [p1, p2] : a)
    //     {
    //         cerr << p1 << " " << p2 << ", ";
    //     }
    //     cerr << "\n";
    // }

    auto check = [&](set<array<int, 2>> &pairs) -> int
    {
        vector<int> covered = covered_init;
        int ret = 0;
        int num = (int)pairs.size();
        for (int i = 0; i < num; i++)
        {
            for (auto [p1, p2] : pairs)
            {
                if (covered[p1] == 0 && covered[p2] == 0)
                {
                    ret += 2;
                    covered[p1] = covered[p2] = -1;
                    for (auto under : covering[p1])
                    {
                        covered[under]--;
                    }
                    for (auto under : covering[p2])
                    {
                        covered[under]--;
                    }
                    pairs.erase({p1, p2});
                    break;
                }
            }
        }

        return ret;
    };

    int ans = 0;
    for (auto c1 : C1)
    {
        for (auto c2 : C2)
        {
            for (auto c3 : C3)
            {
                for (auto c4 : C4)
                {
                    set<array<int, 2>> idxs;
                    for (auto v : c1)
                        idxs.insert(v);
                    for (auto v : c2)
                        idxs.insert(v);
                    for (auto v : c3)
                        idxs.insert(v);
                    for (auto v : c4)
                        idxs.insert(v);

                    // check
                    ans = max(ans, check(idxs));
                }
            }
        }
    }

    cout << ans << "\n";

    return true;
}

int main()
{
    while (solve())
        ;
    return 0;
}
accuracy: 1
time_ms: 400
memory_kb: 3456
final_rank: 1

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

bool solve() {
    int N;
    cin >> N;
    if (N == 0) return false;
    vector<int> X(N), Y(N), R(N), C(N);
    for (int i = 0; i < N; i++) {
        cin >> X[i] >> Y[i] >> R[i] >> C[i];
    }
    vector<bool> dp(1 << N, false);
    dp[0] = true;
    int ans = 0;
    for (int bit = 0; bit < 1 << N; bit++) {
        if (!dp[bit]) continue;
        ans = max(ans, __builtin_popcount(bit));
        vector<int> can_take;
        for (int i = 0; i < N; i++) {
            if (bit >> i & 1) continue;
            if ([&] {
                for (int j = 0; j < i; j++) {
                    if (bit >> j & 1) continue;
                    int dx = X[i] - X[j], dy = Y[i] - Y[j];
                    int r = R[i] + R[j];
                    if (dx * dx + dy * dy < r * r) return false;
                }
                return true;
            } ()) can_take.push_back(i);
        }
        for (int i : can_take) {
            for (int j : can_take) {
                if (i != j && C[i] == C[j]) {
                    dp[bit ^ (1 << i) ^ (1 << j)] = true;
                }
            }
        }
    }
    cout << ans << '\n';
    return true;
}

int main() {
    while (solve()) {}
}
accuracy: 1
time_ms: 1100
memory_kb: 7764
final_rank: 4

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
using P = pair<long long, long long>;
#define rep(i, a, b) for(long long i = (a); i < (b); ++i)
#define rrep(i, a, b) for(long long i = (a); i >= (b); --i)
constexpr long long inf = 4e18;
struct SetupIO {
    SetupIO() {
        ios::sync_with_stdio(0);
        cin.tie(0);
        cout << fixed << setprecision(30);
    }
} setup_io;
using Real = long double;
constexpr Real EPS = Real(1e-8), PI = 3.141592653589793238462643383279L;
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, const 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 Real(1e18);
    sort(ps.begin(), ps.end(), comp_x);
    vector<Point> memo(ps.size());
    auto func = [&](const auto& func, const int l, const int r) -> Real {
        if(r - l <= 1) return Real(1e18);
        const int m = (l + r) >> 1;
        const 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) {
                const 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.emplace_back(l2.a);
    } else {
        res.emplace_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) {
    const 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);
}
struct Circle {
    Point p;
    Real r;
    Circle() = default;
    Circle(const Point& p, const Real& r)
        : p(p), r(r) {}
};
bool is_intersect_cp(const Circle& c, const Point& p) {
    return eq(abs(p - c.p), c.r);
}
bool is_intersect_cl(const Circle& c, const Line& l) {
    return sign(c.r - dist_lp(l, c.p)) >= 0;
}
int is_intersect_cc(Circle c1, Circle c2) {
    if(c1.r < c2.r) swap(c1, c2);
    const Real d = abs(c1.p - c2.p);
    const int a = sign(d - c1.r - c2.r);
    if(a >= 0) return a + 3;
    return sign(d - c1.r + c2.r) + 1;
}
vector<Point> intersection_cl(const Circle& c, const Line& l) {
    const Point h = projection(l, c.p);
    const Point e = (l.b - l.a) / abs(l.b - l.a);
    vector<Point> res;
    if(!is_intersect_cl(c, l)) return res;
    if(eq(dist_lp(l, c.p), c.r)) {
        res.emplace_back(h);
    } else {
        const Real b = sqrt(c.r * c.r - norm(h - c.p));
        res.emplace_back(h - e * b);
        res.emplace_back(h + e * b);
    }
    return res;
}
vector<Point> intersection_cs(const Circle& c, const Segment& s) {
    const vector<Point> cand = intersection_cl(c, Line(s));
    vector<Point> res;
    for(const Point& p : cand) {
        if(ccw(s.a, s.b, p) == 0) {
            res.emplace_back(p);
        }
    }
    return res;
}
vector<Point> intersection_cc(const Circle& c1, const Circle& c2) {
    const Real d = abs(c1.p - c2.p);
    const Real a = acos((c1.r * c1.r + d * d - c2.r * c2.r) / (Real(2.0) * c1.r * d));
    const Real t = atan2(c2.p.imag() - c1.p.imag(), c2.p.real() - c1.p.real());
    vector<Point> res;
    if(is_intersect_cc(c1, c2) % 4 == 0) return res;
    if(eq(a, 0.0)) {
        res.emplace_back(c1.p + rot(Point(c1.r, 0.0), t));
    } else {
        res.emplace_back(c1.p + rot(Point(c1.r, 0.0), t + a));
        res.emplace_back(c1.p + rot(Point(c1.r, 0.0), t - a));
    }
    return res;
}
vector<Point> tangent_cp(const Circle& c, const Point& p) {
    return intersection_cc(c, Circle(p, sqrt(norm(p - c.p) - c.r * c.r)));
}
vector<Line> tangent_cc(Circle c1, Circle c2) {
    vector<Line> res;
    if(c1.r < c2.r) swap(c1, c2);
    const Real r = abs(c2.p - c1.p);
    if(eq(r, 0.0)) return res;
    const Point u = (c2.p - c1.p) / r;
    const Point v = rot(u, PI * 0.5);
    for(const Real s : {Real(1.0), Real(-1.0)}) {
        const Real h = (c1.r + c2.r * s) / r;
        if(eq(abs(h), Real(1.0))) {
            res.emplace_back(c1.p + u * c1.r, c1.p + (u + v) * c1.r);
        } else if(abs(h) < Real(1.0)) {
            const Point uu = u * h, vv = v * sqrt(Real(1.0) - h * h);
            res.emplace_back(c1.p + (uu + vv) * c1.r, c2.p - (uu + vv) * c2.r * s);
            res.emplace_back(c1.p + (uu - vv) * c1.r, c2.p - (uu - vv) * c2.r * s);
        }
    }
    return res;
}
Circle inscribed_circle(const Point& a, const Point& b, const Point& c) {
    const Real A = abs(b - c), B = abs(c - a), C = abs(a - b);
    const Point x = Point((a * A + b * B + c * C) / (A + B + C));
    const Real r = dist_sp(Segment(a, b), x);
    return Circle(x, r);
}
Circle circumscribed_circle(const Point& a, const Point& b, const Point& c) {
    const Point m1((a + b) / 2.0), m2((b + c) / 2.0);
    const Point v((b - a).imag(), (a - b).real()), w((b - c).imag(), (c - b).real());
    const Line s(m1, Point(m1 + v)), t(m2, Point(m2 + w));
    const Point x = intersection_ll(s, t)[0];
    return Circle(x, abs(a - x));
}
Circle appollonius(const Point& p1, const Point& p2, const Real& a, const Real& b) {
    const Point q1 = (p1 * b + p2 * a) / (a + b), q2 = (-p1 * b + p2 * a) / (a - b);
    return Circle((q1 + q2) * 0.5, abs(q1 - q2) * 0.5);
}
Real area_cc(const Circle& c1, const Circle& c2) {
    const Real d = abs(c1.p - c2.p);
    if(c1.r + c2.r <= d + EPS) return 0.0;
    if(d <= abs(c1.r - c2.r) + EPS) {
        const Real r = min(c1.r, c2.r);
        return r * r * PI;
    }
    const Real rc = (d * d + c1.r * c1.r - c2.r * c2.r) / (Real(2.0) * d);
    const Real theta = acos(rc / c1.r);
    const Real phi = acos((d - rc) / c2.r);
    return c1.r * c1.r * theta + c2.r * c2.r * phi - d * c1.r * sin(theta);
}
Real area_pc(const vector<Point>& ps, const Circle& c) {
    auto calc_element = [&](const Point& a, const Point& b, const Real& r, const bool triangle) -> Real {
        if(triangle) return cross(a, b) / 2.0;
        const Point tmp = b * Point(a.real(), -a.imag());
        const Real ang = atan2(tmp.imag(), tmp.real());
        return r * r * ang / 2.0;
    };
    auto cross_area = [&](const Circle& c, const Point& ia, const Point& ib) -> Real {
        const Point a = ia - c.p, b = ib - c.p;
        if(abs(a - b) < EPS) return 0;
        const bool isin_a = (abs(a) < c.r + EPS);
        const bool isin_b = (abs(b) < c.r + EPS);
        if(isin_a and isin_b) return calc_element(a, b, c.r, true);
        const Circle oc(Point(0.0, 0.0), c.r);
        const Segment seg(a, b);
        const vector<Point> cr = intersection_cs(oc, seg);
        if(cr.empty()) return calc_element(a, b, c.r, false);
        const Point s = cr[0], t = cr.back();
        return calc_element(s, t, c.r, true) + calc_element(a, s, c.r, isin_a) + calc_element(t, b, c.r, isin_b);
    };
    const int n = (int)ps.size();
    if(n < 3) return 0.0;
    Real S = 0;
    for(int i = 0; i < n; ++i) {
        S += cross_area(c, ps[i], ps[(i + 1) % n]);
    }
    return S;
}
int main(void) {
    while(1) {
        int n;
        cin >> n;
        if(n == 0) break;
        vector<Circle> c(n);
        vector<int> col(n);
        rep(i, 0, n) {
            cin >> c[i].p >> c[i].r >> col[i];
        }
        vector<vector<int>> intersect(n, vector<int>(n));
        rep(i, 0, n) {
            rep(j, 0, n) {
                intersect[i][j] = is_intersect_cc(c[i], c[j]);
            }
        }
        vector<int> dp(1 << n, -1);
        auto dfs = [&](auto& dfs, int mask) -> int {
            if(dp[mask] != -1) return dp[mask];
            int res = 0;
            rep(i, 0, n) {
                if(!(mask & (1 << i))) continue;
                rep(j, i + 1, n) {
                    if(!(mask & (1 << j))) continue;
                    if(col[i] != col[j]) continue;
                    if(intersect[i][j] <= 2) continue;
                    bool flag = true;
                    rep(k, 0, n) {
                        if(!(mask & (1 << k))) continue;
                        if(k < i and intersect[k][i] <= 2) flag = false;
                        if(k < j and intersect[k][j] <= 2) flag = false;
                    }
                    if(flag) {
                        int nxt = mask xor (1 << i) xor (1 << j);
                        res = max(res, 2 + dfs(dfs, nxt));
                    }
                }
            }
            return dp[mask] = res;
        };
        cout << dfs(dfs, (1 << n) - 1) << '\n';
    }
}
accuracy: 1
time_ms: 1530
memory_kb: 68932
final_rank: 13

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 rep(i,n) for (int i = 0; i < (n); ++i)

    typedef long long ll;
    typedef unsigned long long ull;
    using P=pair<ll,ll>;
    using T=tuple<int,int,int>;
    const int INF=1001001001;
    const ll INFL=1e18;
    const int mod=998244353;

	struct Point{
		int x,y,r,c;
		Point(){}
		Point(int x,int y,int r,int c):x(x),y(y),r(r),c(c){}
	};

	bool covered(Point& a,Point& b){
		return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)<(a.r+b.r)*(a.r+b.r);
	}
	
	bool isTop(int i,vector<Point>& ps,int vis){
		int n=ps.size();
		rep(j,i){
			if(vis>>j&1)continue;
			if(covered(ps[i],ps[j]))return false;
		}
		return true;
	}

	int dfs(int vis,vector<Point>& ps,map<int,int>& dp){
		if(dp.count(vis))return dp[vis];

		int n=ps.size();
		int mx=0;
		rep(i,n){
			rep(j,i){
				if(vis>>i&1 ||vis>>j&1)continue;
				if(!isTop(i,ps,vis)||!isTop(j,ps,vis))continue;
				if(ps[i].c!=ps[j].c)continue;
				int cnt=2+dfs(vis|1<<i|1<<j,ps,dp);
				chmax(mx,cnt);
			}
		}
		return dp[vis]=mx;
	}
	
    bool solve(){
		int n;
		cin>>n;
		if(!n)return false;

		vector<Point>ps(n);
		rep(i,n){
			int x,y,r,c;
			cin>>x>>y>>r>>c;
			ps[i]=Point(x,y,r,c);
		}

		int vis=0;
		map<int,int>dp;
		int ans=dfs(vis,ps,dp);
		cout<<ans<<endl;
		return true;
    }

    int main(){
        ios_base::sync_with_stdio(false);
        cin.tie(NULL);

		while(1){
			if(!solve())break;
		}
		
        return 0;
    }
accuracy: 1
time_ms: 5500
memory_kb: 52608
final_rank: 20

code_snippet: #include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <unordered_set>
#include <vector>

using namespace std;
using ll = long long;

bool IsTop(const vector<int>& over, const int disks, const int disk) {
	return !(over[disk] & disks);
}

void chmax(int& a, const int b) {
	if (a < b) a = b;
}

int _CountRemovableDisks(const vector<int>& over, const vector<int>& c, const int disks, vector<int>& memo) {
	if (memo[disks] >= 0) return memo[disks];

	int ans = 0;

	for (int i = 0; i < over.size(); i++) {
		if (!(disks & (1 << i)) || !IsTop(over, disks, i)) continue;
		for (int j = 0; j < i; j++) {
			if (!(disks & (1 << j)) || !IsTop(over, disks, j)) continue;
			if (c[i] != c[j]) continue;
			chmax(ans, _CountRemovableDisks(over, c, disks ^ ((1 << i) | (1 << j)), memo) + 2);
		}
	}

	memo[disks] = ans;
	return ans;
}

int CountRemovableDisks(const vector<int>& over, const vector<int>& c, const int disks) {
	vector<int> memo(1 << (over.size()), -1);
	return _CountRemovableDisks(over, c, disks, memo);
}

bool Solve() {
	int n;
	cin >> n;
	if (n == 0) return false;
	vector<int> x(n), y(n), r(n), c(n);
	for (int i = 0; i < n; i++) {
		cin >> x[i] >> y[i] >> r[i] >> c[i];
	}

	vector<int> over(n, 0);
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < i; j++) {
			if ((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]) < (r[i] + r[j]) * (r[i] + r[j])) {
				over[i] |= 1 << j;
			}
		}
	}

	int ans = CountRemovableDisks(over, c, (1 << n) - 1);
	cout << ans << endl;

	return true;
}

int main(void) {
	while (Solve());
	return 0;
}
accuracy: 1
time_ms: 520
memory_kb: 68660
final_rank: 10

code_snippet: #include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <unordered_set>
#include <vector>

using namespace std;
using ll = long long;

bool IsTop(const vector<int>& over, const int disks, const int disk) {
	return !(over[disk] & disks);
}

void chmax(int& a, const int b) {
	if (a < b) a = b;
}

int _CountRemovableDisks(const vector<int>& over, const vector<int>& c, const int disks, vector<int>& memo) {
	if (memo[disks] >= 0) return memo[disks];

	int ans = 0;

	for (int i = 0; i < over.size(); i++) {
		if (!(disks & (1 << i)) || !IsTop(over, disks, i)) continue;
		for (int j = 0; j < i; j++) {
			if (!(disks & (1 << j)) || !IsTop(over, disks, j)) continue;
			if (c[i] != c[j]) continue;
			chmax(ans, _CountRemovableDisks(over, c, disks ^ ((1 << i) | (1 << j)), memo) + 2);
		}
	}

	memo[disks] = ans;
	return ans;
}

int CountRemovableDisks(const vector<int>& over, const vector<int>& c, const int disks) {
	vector<int> memo(1 << (over.size()), -1);
	return _CountRemovableDisks(over, c, disks, memo);
}

bool Solve() {
	int n;
	cin >> n;
	if (n == 0) return false;
	vector<int> x(n), y(n), r(n), c(n);
	for (int i = 0; i < n; i++) {
		cin >> x[i] >> y[i] >> r[i] >> c[i];
	}

	vector<int> over(n, 0);
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < i; j++) {
			if ((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]) < (r[i] + r[j]) * (r[i] + r[j])) {
				over[i] |= 1 << j;
			}
		}
	}

	int ans = CountRemovableDisks(over, c, (1 << n) - 1);
	cout << ans << endl;

	return true;
}

int main(void) {
	while (Solve());
	return 0;
}
accuracy: 1
time_ms: 530
memory_kb: 68624
final_rank: 11

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...);
}

#include <unistd.h>

namespace fastio {
static constexpr uint32_t SZ = 1 << 17;
char ibuf[SZ];
char obuf[SZ];
char out[100];
// pointer of ibuf, obuf


uint32_t pil = 0, pir = 0, por = 0;

struct Pre {
    char num[10000][4];
    constexpr Pre() : num() {
        for (int i = 0; i < 10000; i++) {
            int n = i;
            for (int j = 3; j >= 0; j--) {
                num[i][j] = n % 10 | '0';
                n /= 10;
            }
        }
    }
} constexpr pre;

inline void load() {
    memmove(ibuf, ibuf + pil, pir - pil);
    pir = pir - pil + fread(ibuf + pir - pil, 1, SZ - pir + pil, stdin);
    pil = 0;
    if (pir < SZ)
        ibuf[pir++] = '\n';
}

inline void flush() {
    fwrite(obuf, 1, por, stdout);
    por = 0;
}

void rd(char &c) {
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
}

void rd(string &x) {
    x.clear();
    char c;
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
    do {
        x += c;
        if (pil == pir)
            load();
        c = ibuf[pil++];
    } while (!isspace(c));
}

template <typename T> void rd_real(T &x) {
    string s;
    rd(s);
    x = stod(s);
}

template <typename T> void rd_integer(T &x) {
    if (pil + 100 > pir)
        load();
    char c;
    do
        c = ibuf[pil++];
    while (c < '-');
    bool minus = 0;
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (c == '-') {
            minus = 1, c = ibuf[pil++];
        }
    }
    x = 0;
    while ('0' <= c) {
        x = x * 10 + (c & 15), c = ibuf[pil++];
    }
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (minus)
            x = -x;
    }
}

void rd(int &x) {
    rd_integer(x);
}
void rd(ll &x) {
    rd_integer(x);
}
void rd(i128 &x) {
    rd_integer(x);
}
void rd(uint &x) {
    rd_integer(x);
}
void rd(ull &x) {
    rd_integer(x);
}
void rd(u128 &x) {
    rd_integer(x);
}
void rd(double &x) {
    rd_real(x);
}
void rd(long double &x) {
    rd_real(x);
}

template <class T, class U> void rd(pair<T, U> &p) {
    return rd(p.first), rd(p.second);
}
template <size_t N = 0, typename T> void rd_tuple(T &t) {
    if constexpr (N < std::tuple_size<T>::value) {
        auto &x = std::get<N>(t);
        rd(x);
        rd_tuple<N + 1>(t);
    }
}
template <class... T> void rd(tuple<T...> &tpl) {
    rd_tuple(tpl);
}

template <size_t N = 0, typename T> void rd(array<T, N> &x) {
    for (auto &d : x)
        rd(d);
}
template <class T> void rd(vector<T> &x) {
    for (auto &d : x)
        rd(d);
}

void read() {}
template <class H, class... T> void read(H &h, T &...t) {
    rd(h), read(t...);
}

void wt(const char c) {
    if (por == SZ)
        flush();
    obuf[por++] = c;
}
void wt(const string s) {
    for (char c : s)
        wt(c);
}
void wt(const char *s) {
    size_t len = strlen(s);
    for (size_t i = 0; i < len; i++)
        wt(s[i]);
}

template <typename T> void wt_integer(T x) {
    if (por > SZ - 100)
        flush();
    if (x < 0) {
        obuf[por++] = '-', x = -x;
    }
    int outi;
    for (outi = 96; x >= 10000; outi -= 4) {
        memcpy(out + outi, pre.num[x % 10000], 4);
        x /= 10000;
    }
    if (x >= 1000) {
        memcpy(obuf + por, pre.num[x], 4);
        por += 4;
    } else if (x >= 100) {
        memcpy(obuf + por, pre.num[x] + 1, 3);
        por += 3;
    } else if (x >= 10) {
        int q = (x * 103) >> 10;
        obuf[por] = q | '0';
        obuf[por + 1] = (x - q * 10) | '0';
        por += 2;
    } else
        obuf[por++] = x | '0';
    memcpy(obuf + por, out + outi + 4, 96 - outi);
    por += 96 - outi;
}

template <typename T> void wt_real(T x) {
    ostringstream oss;
    oss << fixed << setprecision(15) << double(x);
    string s = oss.str();
    wt(s);
}

void wt(int x) {
    wt_integer(x);
}
void wt(ll x) {
    wt_integer(x);
}
void wt(i128 x) {
    wt_integer(x);
}
void wt(uint x) {
    wt_integer(x);
}
void wt(ull x) {
    wt_integer(x);
}
void wt(u128 x) {
    wt_integer(x);
}
void wt(double x) {
    wt_real(x);
}
void wt(long double x) {
    wt_real(x);
}

template <class T, class U> void wt(const pair<T, U> val) {
    wt(val.first);
    wt(' ');
    wt(val.second);
}
template <size_t N = 0, typename T> void wt_tuple(const T t) {
    if constexpr (N < std::tuple_size<T>::value) {
        if constexpr (N > 0) {
            wt(' ');
        }
        const auto x = std::get<N>(t);
        wt(x);
        wt_tuple<N + 1>(t);
    }
}
template <class... T> void wt(tuple<T...> tpl) {
    wt_tuple(tpl);
}
template <class T, size_t S> void wt(const array<T, S> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}
template <class T> void wt(const vector<T> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}

void print() {
    wt('\n');
}
template <class Head, class... Tail> void print(Head &&head, Tail &&...tail) {
    wt(head);
    if (sizeof...(Tail))
        wt(' ');
    print(forward<Tail>(tail)...);
}
void __attribute__((destructor)) _d() {
    flush();
}
} // namespace fastio


using fastio::flush;
using fastio::print;
using fastio::read;

inline void first(bool i = true) {
    print(i ? "first" : "second");
}
inline void Alice(bool i = true) {
    print(i ? "Alice" : "Bob");
}
inline void Takahashi(bool i = true) {
    print(i ? "Takahashi" : "Aoki");
}
inline void yes(bool i = true) {
    print(i ? "yes" : "no");
}
inline void Yes(bool i = true) {
    print(i ? "Yes" : "No");
}
inline void No() {
    print("No");
}
inline void YES(bool i = true) {
    print(i ? "YES" : "NO");
}
inline void NO() {
    print("NO");
}
inline void Yay(bool i = true) {
    print(i ? "Yay!" : ":(");
}
inline void Possible(bool i = true) {
    print(i ? "Possible" : "Impossible");
}
inline void POSSIBLE(bool i = true) {
    print(i ? "POSSIBLE" : "IMPOSSIBLE");
}

/**
 * @brief Fast IO
 */

int main() {
while(1) {
    int N;
    cin >> N;
    if (N == 0) return 0;
    vector<int> X(N), Y(N), R(N), C(N);
    rep(i,0,N) cin >> X[i] >> Y[i] >> R[i] >> C[i], C[i]--;
    vector<int> B(N, 0);
    rep(i,0,N) {
        rep(j,0,i) {
            if ((X[i]-X[j])*(X[i]-X[j])+(Y[i]-Y[j])*(Y[i]-Y[j])<(R[i]+R[j])*(R[i]+R[j])) B[i] |= (1<<j);
        }
    }
    vector<bool> DP(1<<N,false);
    DP[0] = true;
    int ANS = 0;
    rep(i,0,1<<N) {
        if (!DP[i]) continue;
        int COUNT = 0;
        rep(j,0,N) {
            if ((i & (1<<j)) != 0) COUNT++;
        }
        chmax(ANS,COUNT);
        vector<vector<int>> V(4);
        rep(j,0,N) {
            if ((i & (1<<j)) == 0) {
                if ((i & B[j]) == B[j]) V[C[j]].push_back(j);
            }
        }
        rep(j,0,4) {
            rep(k,0,V[j].size()) {
                rep(l,0,k) {
                    DP[i | (1<<V[j][k]) | (1<<V[j][l])] = true;
                }
            }
        }
    }
    cout << ANS << endl;
}
}
accuracy: 1
time_ms: 530
memory_kb: 7528
final_rank: 2

code_snippet: // (⁠◕⁠ᴗ⁠◕⁠✿⁠)

#include <bits/stdc++.h>
// #pragma GCC target("avx2")
// #pragma GCC optimize("O3")
// #pragma GCC optimize("unroll-loops")
#define rep(i, n) for (ll i = 0; i < (n); i++)
#define srep(i, s, n) for (ll i = s; i < (n); i++)
#define len(x) ((int)(x).size())
#define all(x) (x).begin(), (x).end()
using namespace std;
template<typename T> using vc = vector<T>;
template<typename T> using vv = vc<vc<T>>;
template<typename T> using vvv = vv<vc<T>>;
using vi = vc<int>;using vvi = vv<int>; using vvvi = vv<vi>;
using ll = long long;using vl = vc<ll>;using vvl = vv<ll>; using vvvl = vv<vl>;
using ld = long double; using vld = vc<ld>; using vvld = vc<vld>; using vvvld = vc<vvld>;
using uint = unsigned int;
using ull = unsigned long long;
const ld pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
// const ll mod = 1000000007;
const ll mod = 998244353;
inline bool inside(ll y, ll x, ll H, ll W) {return 0 <= (y) and (y) < (H) and 0 <= (x) and (x) < (W); }

#define debug(var)  do{std::cout << #var << " : \n";view(var);}while(0)
template<typename T> void view(T e){cout << e << endl;}
template<typename T> void view(const vc<T>& v){for(const auto& e : v){ cout << e << " "; } cout << endl;}
template<typename T> void view(const vv<T>& vv){ for(const auto& v : vv){ view(v); } }

int solve(int N){
    vi X(N), Y(N), R(N), C(N); rep(i, N) cin >> X[i] >> Y[i] >> R[i] >> C[i];
    vvi mem(4);
    rep(i, N) mem[C[i] - 1].push_back(i);
    vl up(N, 0);
    rep(j, N) rep(i, j){
        if ((X[i] - X[j]) * (X[i] - X[j]) + (Y[i] - Y[j]) * (Y[i] - Y[j]) < (R[i] + R[j]) * (R[i] + R[j])){
            up[j] |= (1 << i);
        }
    }
    int ans = 0;
    vi seen(1 << N, 0);
    function<void(int)> dfs = [&](int cmb){
        if (seen[cmb]) return;
        seen[cmb] = 1;
        ans = max(ans, N - __builtin_popcount(cmb));
        if (ans == N) return;
        for (auto v : mem){
            rep(j, len(v)) rep(i, j) if ((cmb >> v[i]) & 1 && (cmb >> v[j]) & 1){
                if ((up[v[i]] & cmb) == 0 && (up[v[j]] & cmb) == 0){
                    dfs(cmb ^ (1 << v[i]) ^ (1 << v[j]));
                }
            }
        }
    };
    dfs((1 << N) - 1);
    return ans;
}

int main(){
    vi ans;
    while (true){
        int N; cin >> N;
        if (N == 0) break;
        ans.push_back(solve(N));
    }
    for (auto x : ans) cout << x << endl;
}
accuracy: 1
time_ms: 110
memory_kb: 68728
final_rank: 9

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

bool solve() {
    int N;
    std::cin >> N;
    if (N == 0) return false;
    std::vector<int> X(N), Y(N), R(N), C(N);
    for (int i{} ; i < N ; i++) {
        std::cin >> X[i] >> Y[i] >> R[i] >> C[i];
        C[i]--;
    }
    std::vector<int> up(N);
    for (int i{} ; i < N ; i++) {
        for (int j{} ; j < i ; j++) {
            int dx{X[i] - X[j]}, dy{Y[i] - Y[j]};
            int d{dx * dx + dy * dy};
            if (d < (R[i] + R[j]) * (R[i] + R[j])) {
                up[i] |= (1 << j);
            }
        }
    }
    std::vector<bool> dp(1 << N);
    dp[0] = true;
    for (int bit{} ; bit < (1 << N) ; bit++) {
        if (!dp[bit]) continue;
        // std::cout << bit << " go" << std::endl;
        std::vector<std::vector<int>> rem(4);
        for (int i{} ; i < N ; i++) {
            if (bit & (1 << i)) continue;
            if ((up[i] & bit) != up[i]) continue;
            rem[C[i]].push_back(i);
        }
        for (int i{} ; i < 4 ; i++) {
            for (int j{} ; j < (int)rem[i].size() ; j++) {
                for (int k{j + 1} ; k < (int)rem[i].size() ; k++) {
                    dp[bit | (1 << rem[i][j]) | (1 << rem[i][k])] = true;
                }
            }
        }
    }
    int ans{};
    for (int i{} ; i < (1 << N) ; i++) {
        if (dp[i]) ans = std::max(ans, __builtin_popcount(i));
    }
    std::cout << ans << '\n';
    return true;
}

int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    while (solve()) ;
}
accuracy: 1
time_ms: 590
memory_kb: 7488
final_rank: 3