#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const double eps = 1e-6;
template <typename A, typename B>
ostream& operator<<(ostream& os, const pair<A, B>& p) {
  return os << '{' << p.first << ',' << p.second << '}';
}
template <typename T>
struct point {
  T x, y;
  point(T xx = 0, T yy = 0) : x(xx), y(yy) {}
  T len2() const { return x * x + y * y; }
  double len() const { return sqrt(double(len2())); }
  bool operator==(const point<T>& p) const { return x == p.x && y == p.y; }
};
template <typename T>
point<T> operator+(const point<T>& a, const point<T>& b) {
  return point<T>(a.x + b.x, a.y + b.y);
}
template <typename T>
point<T> operator-(const point<T>& a, const point<T>& b) {
  return point<T>(a.x - b.x, a.y - b.y);
}
template <typename T>
T scal(const point<T>& a, const point<T>& b) {
  return a.x * b.x + a.y * b.y;
}
template <typename T>
T vect(const point<T>& a, const point<T>& b) {
  return a.x * b.y - a.y * b.x;
}
template <typename T>
ostream& operator<<(ostream& os, const point<T>& p) {
  return os << '(' << p.x << ',' << p.y << ')';
}
template <typename T>
T gcd(T a, T b) {
  return a > 0 ? gcd(b % a, a) : b;
}
template <typename T>
ostream& operator<<(ostream& os, const vector<T>& c) {
  for (typename vector<T>::const_iterator it = c.begin(); it != c.end(); ++it) {
    os << *it;
  }
  return os;
}
template <typename T>
ostream& operator<<(ostream& os, const set<T>& c) {
  for (typename set<T>::const_iterator it = c.begin(); it != c.end(); ++it) {
    os << *it;
  }
  return os;
}
template <typename T>
ostream& operator<<(ostream& os, const multiset<T>& c) {
  for (typename multiset<T>::const_iterator it = c.begin(); it != c.end();
       ++it) {
    os << *it;
  }
  return os;
}
template <typename K, typename T>
ostream& operator<<(ostream& os, const multimap<K, T>& c) {
  for (typename multimap<K, T>::const_iterator it = c.begin(); it != c.end();
       ++it) {
    os << *it;
  }
  return os;
}
template <typename K, typename T>
ostream& operator<<(ostream& os, const map<K, T>& c) {
  for (typename map<K, T>::const_iterator it = c.begin(); it != c.end(); ++it) {
    os << *it;
  }
  return os;
}
const int maxn = 100100;
vector<int> g[maxn], c[maxn];
int a[maxn], was[maxn], b[maxn], res[maxn];
vector<int> who[2];
int n;
void go(int v) {
  was[v] = 1;
  for (int i = 0; i < g[v].size(); i++) {
    int p = g[v][i];
    if (was[p]) continue;
    a[p] = a[v] ^ 1;
    go(p);
  }
}
void swaphalves() {
  swap(who[0], who[1]);
  for (int i = 0; i < n; i++) {
    a[i] ^= 1;
  }
}
int guy = 0;
set<int> used;
bool fix() {
  if (who[0].size() == 0) return false;
  memset(b, 0, sizeof(b));
  for (int i = 0; i < n; i++) {
    c[i].clear();
    for (int j = 0; j < g[i].size(); j++) {
      int p = g[i][j];
      if (used.count(p)) continue;
      if (a[i] != a[p]) {
        b[i]++;
        c[i].push_back(p);
      }
    }
    sort(c[i].begin(), c[i].end());
  }
  int best = -1;
  for (int i = 0; i < who[0].size(); i++) {
    int v = who[0][i];
    if (best < 0 || (b[best] > b[v])) {
      best = v;
    }
  }
  assert(best >= 0);
  if (b[best] + 2 > who[1].size()) {
    return false;
  }
  sort(who[1].begin(), who[1].end());
  int i1 = 0;
  int i2 = 0;
  int gg[2] = {0, 0};
  int tt = 0;
  for (; tt < 2 && i2 < who[1].size(); i2++) {
    for (; i1 < c[best].size() && c[best][i1] < who[1][i2]; i1++)
      ;
    if (i1 < c[best].size() && c[best][i1] == who[1][i2]) {
      continue;
    }
    gg[tt] = who[1][i2];
    tt++;
  }
  assert(tt == 2);
  res[best] = guy;
  res[gg[0]] = guy;
  res[gg[1]] = guy;
  guy++;
  vector<int> tmp;
  for (int t = 0; t < 2; t++) {
    tmp = who[t];
    who[t].clear();
    for (int i = 0; i < tmp.size(); i++)
      if (tmp[i] != best && tmp[i] != gg[0] && tmp[i] != gg[1]) {
        who[t].push_back(tmp[i]);
      }
  }
  used.insert(best);
  used.insert(gg[0]);
  used.insert(gg[1]);
  return true;
}
int main() {
  int m;
  cin >> n >> m;
  for (int i = 0; i < m; i++) {
    int x, y;
    scanf("%d%d", &x, &y);
    x--;
    y--;
    g[x].push_back(y);
    g[y].push_back(x);
  }
  memset(a, -1, sizeof(a));
  for (int i = 0; i < n; i++)
    if (!was[i]) {
      a[i] = 0;
      go(i);
    }
  for (int i = 0; i < n; i++) {
    who[a[i]].push_back(i);
  }
  if ((who[0].size() % 3) == 2) {
    swaphalves();
  }
  if ((who[0].size() % 3) == 1) {
    if (!fix()) {
      swaphalves();
      if (!fix() || !fix()) {
        puts("NO");
        return 0;
      }
    }
  }
  assert(!(who[0].size() % 3));
  for (int t = 0; t < 2; t++) {
    for (int i = 0; i < who[t].size();) {
      for (int j = 0; j < 3; j++, i++) {
        res[who[t][i]] = guy;
      }
      guy++;
    }
  }
  puts("YES");
  for (int i = 0; i < n; i++) {
    printf("%d ", res[i] + 1);
  }
  puts("");
  return 0;
}
