#include <bits/stdc++.h>
using namespace std;
string toYesNo(bool b) { return b ? "YES" : "NO"; }
class IScanner {
 public:
  virtual int nextInt() const = 0;
};
class CScanner : IScanner {
 public:
  CScanner(std::istream& inStream) {}
  int nextInt() const {
    int result;
    scanf("%d", &result);
    return result;
  }
};
class StreamScanner : IScanner {
 public:
  StreamScanner(std::istream& inStream) : inStream_(inStream) {}
  int nextInt() const {
    int result;
    inStream_ >> result;
    return result;
  }

 private:
  std::istream& inStream_;
};
using Scanner = CScanner;
class Writer {
  std::ostream& out;

 public:
  Writer(std::ostream& out) : out(out) {}
  Writer& operator<<(int value) {
    printf("%d", value);
    return *this;
  }
  Writer& operator<<(long long value) {
    printf("%I64d", value);
    return *this;
  }
  Writer& operator<<(string& value) {
    printf("%s", value.c_str());
    return *this;
  }
  Writer& operator<<(const char* value) {
    printf("%s", value);
    return *this;
  }
  Writer& newline() {
    printf("\n");
    return *this;
  }
};
class UndirectedGraph {
  friend class UndirectedGraphEdgeMarker;
  vector<vector<int>> adj;
  vector<vector<int>> indexMap;
  vector<int> comp;
  vector<int> color;
  vector<int> deg;
  bool ccBuilt = false;
  int nEdge = 0;
  int n, nComp;
  bool _hasOddCycle = false;
  void dfs(int u, int c) {
    comp[u] = nComp;
    color[u] = c;
    for (auto& v : adj[u]) {
      if (comp[v] == -1) {
        dfs(v, 1 - c);
      } else {
        if (color[v] != 1 - c) {
          _hasOddCycle = true;
        }
      }
    }
  }

 public:
  void init(int n) {
    this->n = n;
    adj.resize(n + 1);
    indexMap.resize(n + 1);
    deg.resize(n + 1);
    for (auto u = 1; u <= n; ++u) {
      adj[u].clear();
      indexMap[u].clear();
    }
    nEdge = 0;
    ccBuilt = false;
  }
  const vector<int>& getAdjacent(int u) const { return adj[u]; }
  void addEdge(int u, int v) {
    assert(1 <= u && u <= n);
    assert(1 <= v && v <= n);
    adj[u].push_back(v);
    adj[v].push_back(u);
    indexMap[u].push_back((int)adj[v].size() - 1);
    indexMap[v].push_back((int)adj[u].size() - 1);
    nEdge++;
    deg[u]++;
    deg[v]++;
    ccBuilt = false;
  }
  int getDeg(int u) const { return deg[u]; }
  int getSize() const { return n; }
  int getNumEdges() const { return nEdge; }
  void buildCC() {
    ccBuilt = true;
    comp.resize(n + 1);
    fill(comp.begin(), comp.end(), -1);
    color.resize(n + 1);
    nComp = 0;
    _hasOddCycle = false;
    for (auto u = 1; u <= n; ++u) {
      if (comp[u] == -1) {
        nComp++;
        dfs(u, 0);
      }
    }
  }
  bool hasOddCycle() {
    assert(ccBuilt);
    return _hasOddCycle;
  }
  int getComponent(int u) {
    assert(ccBuilt);
    return comp[u];
  }
  int getNumberOfComponents() {
    assert(ccBuilt);
    return nComp;
  }
  int getComponentSize(int compId) {
    assert(ccBuilt);
    int cnt = 0;
    for (auto u = 1; u <= n; ++u) {
      if (comp[u] == compId) {
        cnt++;
      }
    }
    return cnt;
  }
};
class UndirectedGraphEdgeMarker {
  UndirectedGraph g;
  vector<vector<bool>> flag;
  int n;
  void set(int u, int i, bool b) {
    assert(i >= 0 && i < g.getAdjacent(u).size());
    flag[u][i] = b;
    int v = g.getAdjacent(u)[i];
    flag[v][g.indexMap[u][i]] = b;
  }

 public:
  UndirectedGraphEdgeMarker(const UndirectedGraph& g) : g(g) {
    n = g.getSize();
    flag.resize(n + 1);
    for (auto u = 1; u <= n; ++u) {
      flag[u].resize(g.getAdjacent(u).size());
    }
  }
  void mark(int u, int i) { set(u, i, true); }
  void unmark(int u, int i) { set(u, i, false); }
  bool test(int u, int i) const {
    assert(i >= 0 && i < g.getAdjacent(u).size());
    return flag[u][i];
  }
};
struct Edge {
  int u, v, count;
  int choose;
  Edge(int u, int v, int count) : u(u), v(v), count(count){};
};
struct Result {
  int bestWeight = 0;
  int currentWeight = 0;
};
class TaskE {
  vector<int> maxWeight[4 + 1][4 + 1];
  bool hasCircuit(const vector<Edge>& edges) {
    UndirectedGraph g;
    g.init(4);
    for (auto& edge : edges) {
      for (auto repeat_var = 0; repeat_var < edge.choose; ++repeat_var) {
        g.addEdge(edge.u, edge.v);
      }
    }
    g.buildCC();
    unordered_set<int> allComps;
    for (auto& edge : edges) {
      if (edge.choose > 0) {
        allComps.insert(g.getComponent(edge.u));
        allComps.insert(g.getComponent(edge.v));
      }
    }
    if (allComps.size() > 1) {
      return false;
    }
    int countOdd = 0;
    for (auto u = 1; u <= 4; ++u) {
      if (g.getDeg(u) % 2 == 1) {
        countOdd++;
      }
    }
    return countOdd == 0 || countOdd == 2;
  }
  void tryAll(vector<Edge>& edges, Result& result, int idx,
              int remainingWeight) {
    if (idx == -1) {
      if (result.currentWeight > result.bestWeight && hasCircuit(edges)) {
        result.bestWeight = result.currentWeight;
      }
      return;
    }
    if (result.currentWeight + remainingWeight <= result.bestWeight) {
      return;
    }
    auto& edge = edges[idx];
    for (int cnt = edge.count; cnt >= 0; cnt--) {
      if (edge.u == edge.v && cnt > 0 && cnt < edge.count) {
        continue;
      }
      if (cnt > 0 && cnt + 2 < edge.count) {
        continue;
      }
      edge.choose = cnt;
      int wgt = maxWeight[edge.u][edge.v][cnt];
      ;
      result.currentWeight += wgt;
      tryAll(edges, result, idx - 1, remainingWeight - wgt);
      result.currentWeight -= wgt;
    };
  }

 public:
  void solve(std::istream& inStream, std::ostream& outStream) {
    Scanner in(inStream);
    Writer out(outStream);
    int nBlocks = in.nextInt();
    vector<int> weight[4 + 1][4 + 1];
    int totalWeight = 0;
    for (auto repeat_var = 0; repeat_var < nBlocks; ++repeat_var) {
      int u = in.nextInt();
      int val = in.nextInt();
      int v = in.nextInt();
      if (u > v) {
        swap(u, v);
      }
      weight[u][v].emplace_back(val);
      totalWeight += val;
    }
    int numEdges[4 + 1][4 + 1];
    vector<Edge> edges;
    for (auto u = 1; u <= 4; ++u) {
      for (auto v = u; v <= 4; ++v) {
        sort(weight[u][v].begin(), weight[u][v].end());
        numEdges[u][v] = weight[u][v].size();
        maxWeight[u][v].clear();
        maxWeight[u][v].push_back(0);
        int nEdges = numEdges[u][v];
        int sum = 0;
        for (auto i = 0; i < nEdges; ++i) {
          sum += weight[u][v][nEdges - 1 - i];
          maxWeight[u][v].push_back(sum);
        }
        edges.push_back(Edge(u, v, nEdges));
      }
    }
    Result result;
    tryAll(edges, result, edges.size() - 1, totalWeight);
    out << result.bestWeight;
  }
};
int main() {
  TaskE solver;
  std::istream& in(std::cin);
  std::ostream& out(std::cout);
  solver.solve(in, out);
  return 0;
}
