#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100100;
int n, m;
vector<int> adj[MAXN];
bool used[MAXN];
int total;
int ans[MAXN];
vector<int> g1, g2;
vector<int> backneis[MAXN];
void dfs(int v, int col) {
  used[v] = true;
  if (col == 1)
    g1.push_back(v);
  else
    g2.push_back(v);
  for (int i = 0; i < adj[v].size(); i++) {
    int k = adj[v][i];
    if (!used[k]) {
      dfs(k, 3 - col);
    }
  }
}
void add_triple(int a, int b, int c) {
  ans[a] = total;
  ans[b] = total;
  ans[c] = total;
  total++;
}
bool vec_erase(vector<int> &v, int z) {
  for (int i = 0; i < v.size(); i++) {
    if (v[i] == z) {
      v.erase(v.begin() + i);
      return true;
    }
  }
  return false;
}
void extract_triple(vector<int> &v1, vector<int> &v2, int l1, int r1, int r2) {
  add_triple(l1, r1, r2);
  assert(vec_erase(v1, l1));
  assert(vec_erase(v2, r1));
  assert(vec_erase(v2, r2));
}
int tmp[MAXN];
bool find_two_other(vector<int> &v1, vector<int> &v2, int &r1, int &r2) {
  for (int i = 0; i < v2.size(); i++) tmp[v2[i]] = 0;
  for (int i = 0; i < v1.size(); i++) {
    tmp[v1[i]] = 1;
  }
  r1 = r2 = -1;
  for (int i = 0; i < v2.size(); i++) {
    if (tmp[v2[i]] == 0) {
      if (r1 < 0)
        r1 = v2[i];
      else {
        r2 = v2[i];
        return true;
      }
    }
  }
  return false;
}
bool find_one_other(vector<int> &v1, vector<int> &v2, int &r1) {
  for (int i = 0; i < v2.size(); i++) tmp[v2[i]] = 0;
  for (int i = 0; i < v1.size(); i++) {
    tmp[v1[i]] = 1;
  }
  r1 = -1;
  for (int i = 0; i < v2.size(); i++) {
    if (tmp[v2[i]] == 0) {
      r1 = v2[i];
      return true;
    }
  }
  return false;
}
bool solve(vector<int> &v1, vector<int> &v2) {
  for (int i = 0; i < v1.size(); i++) {
    int lv = v1[i];
    if (adj[lv].size() <= v2.size() - 2) {
      int rv1, rv2;
      assert(find_two_other(adj[lv], v2, rv1, rv2));
      extract_triple(v1, v2, lv, rv1, rv2);
      return true;
    } else if (adj[lv].size() == v2.size() - 1) {
      int rv;
      assert(find_one_other(adj[lv], v2, rv));
      backneis[rv].push_back(lv);
    }
  }
  int rv_tmp = -1;
  for (int i = 0; i < v2.size(); i++) {
    int rv = v2[i];
    if (backneis[rv].size() >= 2) {
      if (rv_tmp < 0)
        rv_tmp = rv;
      else {
        extract_triple(v2, v1, rv_tmp, backneis[rv_tmp][0],
                       backneis[rv_tmp][1]);
        extract_triple(v2, v1, rv, backneis[rv][0], backneis[rv][1]);
        return true;
      }
    }
  }
  return false;
}
int main() {
  scanf("%d%d", &n, &m);
  for (int i = 0; i < m; i++) {
    int a, b;
    scanf("%d%d", &a, &b);
    a--;
    b--;
    adj[a].push_back(b);
    adj[b].push_back(a);
  }
  for (int i = 0; i < n; i++) {
    if (!used[i]) {
      dfs(i, 1);
    }
  }
  int p = g1.size();
  int q = g2.size();
  bool ok = false;
  if (p % 3 == 1 && q % 3 == 2) {
    ok = solve(g1, g2);
  } else if (p % 3 == 2 && q % 3 == 1) {
    ok = solve(g2, g1);
  } else {
    ok = true;
  }
  if (!ok)
    printf("NO\n");
  else {
    for (int i = 0; i < g1.size(); i += 3) {
      assert(i + 2 < g1.size());
      add_triple(g1[i], g1[i + 1], g1[i + 2]);
    }
    for (int i = 0; i < g2.size(); i += 3) {
      assert(i + 2 < g2.size());
      add_triple(g2[i], g2[i + 1], g2[i + 2]);
    }
    printf("YES\n");
    for (int i = 0; i < n; i++) printf("%d ", ans[i] + 1);
    printf("\n");
  }
  return 0;
}
