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

#define all(a) (a).begin(), (a).end()
#define rall(a) (a).rbegin(), (a).rend()
#define MIN(a) *min_element(all(a))
#define MAX(a) *max_element(all(a))
#define SUM(a) accumulate(all(a), 0LL)
#define REP(i, n) for(int (i)=0; (i)<(n); (i)++)
#define RREP(i, n) for(int (i)=(n)-1; (i)>=0; (i)--)
#define FOR(i, m, n) for(int (i)=(m); (i)<(n); i++)
#define FORR(i, m, n) for(int (i)=(n)-1; (i)>=(m); i--)
#define debug(x) cerr << #x << " = " << x << endl
//#define int long long

typedef long long ll;
//ll const MOD = 998244353;
ll const MOD = 1e9+7;
int const inf = 1e9;
ll const INF = 1e18;

inline void ios_(){cin.tie(0); ios::sync_with_stdio(false);}
template<typename T> int size(const T& a){return (int)a.size();}
template<typename T> T Div(T a, T b){return (a + b - 1) / b;}
template<typename T> bool chmin(T& a, const T& b){if(a > b){a = b; return true;} return false;}
template<typename T> bool chmax(T& a, const T& b){if(a < b){a = b; return true;} return false;}

//Graph in adjcency list
struct Graph{
    struct Edge{
        int from;
        int to;
        ll cost;
        Edge(int a, ll b) : to(a), cost(b) {}
        Edge(int a, int b, ll c) : from(a), to(b), cost(c) {}
    };

    int N;  //頂点数
    vector<vector<Edge>> edges;   //隣接リスト
    vector<ll> dist;   //ノードの深さ
    ll diam;            //木の直径
    vector<int> deg;    //入次数
    vector<int> cycle;  //サイクル
    vector<int> tag;
    vector<int> cnt;
    vector<int> ans;

    Graph(int n) : N(n), edges(n), dist(n, INF), diam(INF), deg(n, 0), tag(n), cnt(4, 0), ans(n, -1){
    }

    void connect(int a, int b, ll c){   //辺を追加
        edges[a].emplace_back(Edge(a, b, c));
        deg[b]++;
    }

    bool visited(int v){
        return dist[v] < INF;
    }

    void resetDist(){
        for(int i=0; i<N; i++){
            dist[i] = INF;
        }
    }

    void dfs(int p, int v, ll d){    //深さ優先探索
        if(visited(v)) return;

        dist[v] = d;

        for(auto e : edges[v]){
            if(e.to == p) continue;
            dfs(v, e.to, d + e.cost);
        }
    }

    void dfs(int p, int v, ll d, int t){    //深さ優先探索
        if(visited(v)) return;

        dist[v] = d;

        tag[v] = t;
        cnt[t]++;

        t = 3 - t;

        for(auto e : edges[v]){
            if(e.to == p) continue;
            dfs(v, e.to, d + e.cost, t);
        }
    }

    int cnt_max(int t){
        return (N + (3 - t)) / 3;
    }

    void solve() {
        vector<int> num = {0,1,2,3};
        if(cnt[1] < cnt_max(1)){
            REP(i, N){
                if(tag[i] == 1){
                    ans[i] = num[3];
                    num[3] += 3;
                }
            }
            REP(i, N){
                if(ans[i] == -1){
                    REP(j, 3){
                        if(num[j+1] <= N){
                            ans[i] = num[j+1];
                            num[j+1] += 3;
                            break;
                        }
                    }
                }
            }
        }else if(cnt[2] < cnt_max(2)){
            REP(i, N){
                if(tag[i] == 2){
                    ans[i] = num[3];
                    num[3] += 3;
                }
            }
            REP(i, N){
                if(ans[i] == -1){
                    REP(j, 3){
                        if(num[j+1] <= N){
                            ans[i] = num[j+1];
                            num[j+1] += 3;
                            break;
                        }
                    }
                }
            }
        }else{
            REP(i, N){
                if(tag[i] == 1){
                    if(num[1] <= N){
                        ans[i] = num[1];
                        num[1] += 3;
                    }else{
                        ans[i] = num[3];
                        num[3] += 3;
                    }
                }else{
                    if(num[2] <= N){
                        ans[i] = num[2];
                        num[2] += 3;
                    }else{
                        ans[i] = num[3];
                        num[3] += 3;
                    }
                }
            }
        }

        REP(i, N){
            cout << ans[i] << " ";
        }
        cout << endl;

        /*
        if(cnt[1] < cnt_max(1)){
            REP(i, N){
                if(tag[i] == 1){
                    tag[i] = 3;
                    cnt[3]++;
                    cnt[1]--;
                }
            }
            REP(i, N){
                if(cnt[2] <= cnt_max(2)) break;
                if(tag[i] == 2){
                    if(cnt[1] < cnt_max(1)){
                        tag[i] = 1;
                        cnt[1]++;
                        cnt[2]--;
                    }else if(cnt[3] < cnt_max(3)){
                        tag[i] = 3;
                        cnt[3]++;
                        cnt[2]--;
                    }
                }
            }
        }else if(cnt[2] < cnt_max(2)){
            REP(i, N){
                if(tag[i] == 2){
                    tag[i] = 3;
                    cnt[3]++;
                    cnt[2]--;
                }
            }
            REP(i, N){
                if(cnt[1] <= cnt_max(1)) break;
                if(tag[i] == 1){
                    if(cnt[2] < cnt_max(2)){
                        tag[i] = 2;
                        cnt[2]++;
                        cnt[1]--;
                    }else if(cnt[3] < cnt_max(3)){
                        tag[i] = 3;
                        cnt[3]++;
                        cnt[1]--;
                    }
                }
            }
        }else{
            REP(i, N) {
                if(cnt[1] > cnt_max(1)){
                    cnt[1]--;
                    cnt[3]++;
                    tag[i] = 3;
                }else if(cnt[2] > cnt_max(2)){
                    cnt[2]--;
                    cnt[3]++;
                    tag[i] = 3;
                }else{
                    break;
                }
            }
        }
        REP(i, N){
            int t = tag[i];
            assert(num[t] <= N);
            cout << num[t] << " ";
            num[t] += 3;
        }
        cout << endl;
        */
    }

    void bfs(int v){        //幅優先探索
        queue<Edge> que;
        for(auto e : edges[v]){
            que.push(e);
            dist[e.to] = e.cost;
        }

        dist[v] = 0;

        while(!que.empty()){
            int u = que.front().to;
            ll d = que.front().cost;
            que.pop();

            for(auto e : edges[u]){
                if(!visited(e.to)){
                    que.push(Edge(e.to, d + e.cost));
                    dist[e.to] = d + e.cost;
                }
            }
        }
    }

    ll diameter(){  //木の直径を求める
        if(diam != INF) return diam;  //計算済みの場合

        dfs(-1, 0, 0);  //頂点0からの最遠頂点vを求める
        int v = max_element(all(dist)) - dist.begin();

        resetDist();

        dfs(-1, v, 0);  //vからの最遠頂点を求める
        int u = max_element(all(dist)) - dist.begin();

        return diam = MAX(dist);   //この2頂点間の距離が直径
    }

    void dijkstra(int start){   //ダイクストラ法
        dist[start] = 0;

        auto fn = [](Edge a, Edge b){return a.cost > b.cost;};
        priority_queue<Edge, vector<Edge>, decltype(fn)> que(fn);

        for(auto e : edges[start]) que.push(e);

        while(!que.empty()){
            auto v = que.top().to;
            auto d = que.top().cost;
            que.pop();

            if(visited(v)) continue;
            else dist[v] = d;
            
            for(auto e : edges[v]){
                if(dist[e.to] == INF){
                    que.push(Edge(e.to, e.cost + d));
                }
            }
        }
    }

    bool BellmanFord(int start){    //ベルマンフォード法 (負の閉路がある場合はtrueを返す)
        dist[start] = 0;
        REP(i, N){
            REP(j, N){
                for(auto k : edges[j]){
                    if(visited(j) && dist[j] + k.cost < dist[k.to]){
                        if(i == N-1) return true;  //N回目にも更新があるなら負の閉路が存在
                        dist[k.to] = dist[j] + k.cost;
                    }
                }
            }
        }
        return false;
    }

    vector<int> topologicalSort(){ //トポロジカルソート
        queue<int> que;
        REP(i, N){
            if(deg[i] == 0) que.push(i);
        }

        vector<int> result;

        while(!que.empty()){
            auto v = que.front();
            que.pop();
            result.push_back(v);
            for(auto e : edges[v]){
                deg[e.to]--;
                if(deg[e.to] == 0) que.push(e.to);
            }
        }
        
        return result;
    }

    void findCycle(int p, int v) {
        static vector<bool> seen(N, false), finished(N, false);
        static int pos = -1;

        seen[v] = true;
        cycle.push_back(v);

        for(auto e : edges[v]){
            if(e.to == p || finished[e.to]) continue;
            if(seen[e.to] && !finished[e.to]){
                pos = e.to;
                return;
            }
            findCycle(v, e.to);

            if(pos != -1) return;
        }

        cycle.pop_back();
        finished[v] = true;
    }
};

struct MatrixGraph : Graph {
    MatrixGraph(int n) : Graph(n) {
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < n; j++) {
                edges[i].emplace_back(Edge(i, j, (i == j ? 0 : INF)));
            }
        }
    }

    void connect(int a, int b, ll c) {
        edges[a][b].cost = c;
    }

    void WarshallFloyd() {
        for(int k = 0; k < N; k++) {
            for(int i = 0; i < N; i++) {
                for(int j = 0; j < N; j++) {
                    ll d = edges[i][k].cost + edges[k][j].cost;
                    if(d < edges[i][j].cost) {
                        edges[i][j].cost = d;
                    }
                }
            }
        }
    }
};

struct GridGraph : Graph {
    int H, W;
    vector<vector<bool>> grid;
    vector<int> dx, dy;

    GridGraph(int h, int w) : Graph(h*w), H(h), W(w), grid(h, vector<bool>(w, false)) {
        dx = {1, 0, -1, 0, 1, 1, -1, -1};
        dy = {0, 1, 0, -1, 1, -1, 1, -1};
    }

    void setGrid(int x, int y, bool b) {
        grid[x][y] = b;
    }

    bool out(int x, int y) {
        return (x < 0 || x >= H || y < 0 || y >= W);
    }

    int to1D(int x, int y) {
        return x * W + y;
    }

    void connectAll(bool diagonal) {
        for(int x = 0; x < H; x++) {
            for(int y = 0; y < W; y++) {
                if(!grid[x][y]) continue;

                for(int k = 0; k < (diagonal ? 8 : 4); k++) {
                    int nx = x + dx[k], ny = y + dy[k];
                    if(out(nx, ny) || !grid[nx][ny]) continue;
                    connect(to1D(x, y), to1D(nx, ny), 1);
                }
            }
        }
    }
};


signed main(){
    int n;
    cin >> n;

    Graph G(n);
    REP(i, n-1){
        int a, b;
        cin >> a >> b;
        G.connect(a-1, b-1, 1);
        G.connect(b-1, a-1, 1);
    }

    G.dfs(-1, 0, 0, 1);
    G.solve();

    return 0;
}


Measured memory_bytes: 21864