#include<bits/stdc++.h>
using namespace std; using LL = long long;
#define FOR(i,s,t) for(int i = s; i < t ; ++i)
#define debug(a) cerr<<#a":="<<a<<endl;
#define SZ(a) (int)a.size()

// いや13ケース目なんやねん
#define int long long 
struct UnionFind {
	vector<int> data;
	UnionFind(int n) { data.assign(n, -1); }
	bool unionSet(int x, int y) {
		x = root(x); y = root(y);
		if (x != y) {
			if (data[y] < data[x]) swap(x, y);
			data[x] += data[y]; data[y] = x;
		}
		return x != y;
	}
	bool same(int x, int y) { return root(x) == root(y); }
	int root(int x) { return data[x] < 0 ? x : data[x] = root(data[x]); }
	int size(int x) { return -data[root(x)]; }
};

struct TwoEdgeConnectedComponent {
	UnionFind uf;
	vector<vector<int>> g;
	vector<pair<int, int>> edges;
	vector<int> used, ord, low, comp;

	TwoEdgeConnectedComponent(int v) : uf(v), g(v), used(v, 0), comp(v), ord(v), low(v) {}

	void add_edge(int x, int y) {
		g[x].push_back(y);
		g[y].push_back(x);
		edges.push_back(minmax(x, y));
	}

	void dfs(int idx, int& k, int par = -1) {
		used[idx] = true;
		ord[idx] = k++;
		low[idx] = ord[idx];

		for (auto &to : g[idx]) {
			if (!used[to]) {
				dfs(to, k, idx);
				low[idx] = min(low[idx], low[to]);
				if (ord[idx] >= low[to]) uf.unionSet(idx, to);
			}
			else if (to != par) {
				low[idx] = min(low[idx], ord[to]);
			}
		}
	}

	// build, renew resG(empty -> tree), return color
	vector<int> get_graph(vector<vector<int>>& resG, int root = 0) {
		int kk = 0;
		dfs(root, kk);

		int ptr = 0;
		vector<int> color(g.size());
		for (int i = 0; i < (int)g.size(); i++) {
			if (i == uf.root(i)) color[i] = ptr++;
		}

		resG.resize(ptr);
		for (int i = 0; i < (int)g.size(); i++) {
			comp[i] = color[uf.root(i)];
		}
		for (auto &e : edges) {
			int x = comp[e.first], y = comp[e.second];
			if (x == y) continue;
			resG[x].push_back(y);
			resG[y].push_back(x);
		}
		return comp;
	}
};

LL W[100005];
using PLL = pair<LL, LL>;
LL f(const vector<vector<PLL>> &G, int v, int p = -1) {
	FOR(i, 0, SZ(G[v])) {
		int nx = G[v][i].first;
		if (nx == p)continue;
		LL w = G[v][i].second;
		W[v] += w;
		W[v] += f(G, nx, v);
	}
	return W[v];
}

signed main() {
	cin.tie(0);
	ios_base::sync_with_stdio(false);

	LL N, M; cin >> N >> M;
	using tp = tuple<LL, LL, LL>;
	vector<tp>edge(M);
	TwoEdgeConnectedComponent bc(N);

	LL Wsum = 0;
	FOR(i, 0, M) {
		int a, b, c; cin >> a >> b >> c;
		a--, b--;
		edge[i] = tp(a, b, c);
		bc.add_edge(a, b);
		Wsum += c;
	}
	vector<vector<int>>BG;
	vector<int>cmp = bc.get_graph(BG);
	// 成分の重みを作る
	const LL INF = 1e18;
	PLL ans = PLL(1000000, 1000000);
	LL subMi = 2e18;

	{
		FOR(i, 0, M) {
			LL x, y, w;
			tie(x, y, w) = edge[i];

			if (subMi> Wsum-w) {
				subMi = Wsum - w;
				ans = PLL(x, y);
			}
			else if(subMi== Wsum - w){
				ans = min(ans,PLL(x, y));
			}
		}
	}
	{
		// 木の頂点がアレなら付与
		vector<vector<PLL>>G(SZ(BG));
		using ftp = tuple<int, int, int, int, int>;
		vector<ftp>ed;
		FOR(i, 0, M) {
			LL x, y, w;
			tie(x, y, w) = edge[i];
			if (cmp[x] == cmp[y]) {
				W[cmp[x]] += w;
			}
			else {
				G[cmp[x]].push_back(PLL(cmp[y], w));
				G[cmp[y]].push_back(PLL(cmp[x], w));
				ed.push_back(ftp(x,y,cmp[x], cmp[y], w));
			}
		}

		// 持ち上げればできる
		// 辺と頂点についてやる
		f(G, 0);
		// できた

		LL allW = W[0];
		// 引けば全部出せる気がするのでできる
		// PLL ans
		FOR(i, 0, SZ(ed)) {
			LL xid, yid, x, y, w;
			tie(xid, yid, x, y, w) = ed[i];
			if (W[x] > W[y]) {
				swap(x, y);
			}
			LL WA = W[x];
			LL WB = max(0LL,allW - w - WA);
			//cout <<"comp id is"<< x << ", " << y << " w sz is :";
			//cout << WA << ", " << WB;
			//cout <<"|| recover id :"<< xid << ", " << yid << endl;

			LL subs = abs(WA - WB);
			if (subMi > subs) {
				subMi = subs;
				ans = PLL(xid,yid);
			}
			else if (subMi == subs) {
				ans = min(ans,PLL(xid, yid));
			}
		}
		//debug(subMx);

	}
	//if (ans.first > ans.second) {
	//	assert(0);
	//}
	cout << ans.first+1 << " " << ans.second+1 << endl;
}



Measured memory_bytes: 35752