#include <bits/stdc++.h>
using namespace std;
 
#define int long long
#define pb push_back
#define eb emplace_back
#define fi first
#define se second
#define fr(i,j,k) for(i = j; i < (k); i++)
#define all(x) x.begin(), x.end()
#define el '\n'
#define remax(a,b) a = max(a, b)
#define remin(a,b) a = min(a, b)
#define sz(x) int32_t(x.size())
 
typedef long double ld;
typedef pair<int,int> pii;
typedef vector<int> vi;
typedef vector<vi> vvi;
typedef vector<pii> vpi;
// --------------------------------------------------------------------
 
const pii dxy[] = { {-1, 0}, {1, 0}, {0, 1}, {0, -1} };
const int mod = 1e9 + 7;
const int INF = 1e18;
const ld EPS = 1e-9;
const int NN = 1e5 + 2;
 
vpi v;
 
struct node {
	int value;
	int i;
};
 
struct segtree {
	const node neutral = {-INF, -INF};
	vector<node> tree;
	int N;
 
	segtree(int _n) : N(_n) {
		tree.assign((N+1)<<2, neutral);
		build(1, 0, N - 1);
	}
 
	node merge(node& a, node& b) {
		if(a.value >= b.value) return {a.value, a.i};
		else return {b.value, b.i};
	}
 
	void pull(int idx) {
		tree[idx] = merge(tree[idx<<1], tree[idx<<1 | 1]);
	}
 
	void build(int idx, int s, int e) {
		if(s == e) {
			tree[idx] = { v[s].se, s };
		} else {
			int mid = s+e >> 1;
			build(idx<<1, s, mid);
			build(idx<<1 | 1, mid + 1, e);
			pull(idx);
		}
	}
 
	node query(int idx, int s, int e, int qs, int qe) {
		if(qe < s or qs > e) {
			return neutral;
		}
		if(qs <= s and e <= qe) {
			return tree[idx];
		}
		int m = s+e >> 1;
		node left = query(idx<<1, s, m, qs, qe);
		node right =  query(idx<<1 | 1, m+1, e, qs, qe);
		return merge(left, right);
	}
 
	void update(int idx, int s, int e, int new_idx, int new_val) {
		if(s == e) {
			tree[idx] = {new_val, new_idx};
		} else{
			int mid = s+e >> 1;
			if(new_idx > mid)
				update(idx<<1 | 1, mid+1, e, new_idx, new_val);
			else
				update(idx<<1, s, mid, new_idx, new_val);
 
			pull(idx);
		}
	}
};

string to_string(string s) {
	return '"' + s + '"';
}
string to_string(const char* s) {
	return to_string((string) s);
}
string to_string(bool b) {
	return (b ? "true" : "false");
}
template <typename A, typename B>
string to_string(pair<A, B> p) {
	return "(" + to_string(p.first) + ", " + to_string(p.second) + ")";
}
template <typename A>
string to_string(A v) {
	bool first = true;  string res = "{";
	for (const auto &x : v) {
		if (!first) { res += ", "; }
		first = false; res += to_string(x);
	}
	res += "}";	  return res;
}
void debug_out() { cerr << endl; }
template <typename Head, typename... Tail>
void debug_out(Head H, Tail... T) {
	cerr << " " << to_string(H);
	debug_out(T...);
}

// #define TRACE

#ifdef TRACE
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]:", debug_out(__VA_ARGS__)
#else
#define debug(...)
#endif
//============================================================================
 
bool solve() {
	int i = 0, j = 0, k = 0, n = 0, m = 0;
	cin >> n;
	vector<string> s(n);
	v.resize(n);
	fr(i, 0, n) {
		cin >> s[i];
		int lst = INF, sm = 0;
		for(int ch : s[i]) {
			int a = ch == ')' ? -1 : 1;
			sm += a;
			remin(lst, sm);
		}
		assert(lst < INF);
		v[i] = {lst, sm};
 
		m += sm;
	}
	if(m != 0) {
		return 0;
	}
	debug(v);
	sort(all(v));
	segtree S(n);
	set<int> present;
	fr(i, 0, n) present.emplace(i);
	int sofar = 0;
	bool can = 1;
	for(int take = 1; take <= n and sofar >= 0; take++) {
		// sofar + leastsum >= 0
		pii q = {-sofar, -INF};
		int idx = lower_bound(all(v), q) - v.begin();
		if(idx >= n) {
			can = 0;
			break;
		}
		node sq = S.query(1, 0, n - 1, idx, n - 1);
		debug(sofar, v[idx], sq.i);

		if(sq.value < 0) {
			/*
			Now the remaining values of any v[i].se is < 0, because the maximum among them is < 0
			for any i, v[i].fi <= v[i].se and both are -ve
			at any step if we choose to take element j, then |sofar| < |v[j].fi| and we update
			sofar = sofar + v[j].se
			This means -sofar can be at most v[j].fi before updating it i.e. taking element j ==> 
			after taking element j -sofar can be at most v[j].se

			This can be modeled as line segements (1 for each element) parallel to x-axis with
			end points = { v[i].fi, v[i].se } [left to right] and the pointer which has some initial position having 
			co-ordiante < min(v[i].fi) for all i. The pointer moves towards right covering each line segment
			and after taking line segment i the position of pointer is updated as incrementing by |v[i].se|.
			==> after taking line segment i the position of pointer will be <= v[i].fi - v[i].se ==> the righmost 
			position that the pointer can end up is v[i].fi-v[i].se
			And since we want to take all line segment, it is optimal to take them in order of v[i].fi-v[i].se
			from left to right i.e. in ascending order. If at any point we miss any line segment, then answer is NO
			*/
			break;
		}
 
		sofar += sq.value;
		S.update(1, 0, n - 1, sq.i, -INF);
		present.erase(sq.i);
	}
	if(can and !present.empty()) {
		/*
		Remaining values are < 0
		*/
		vector<pair<int, pii>> lines;
		for(auto &p : present) {
			lines.pb( { abs(v[p].fi - v[p].se), {v[p].fi, v[p].se} } );
		}
		sort(all(lines));
		reverse(all(lines));

		debug(lines, sofar);
		for(auto& f : lines) {
			if(-sofar > f.se.fi) {
				can = 0;
				 break;
			}
			sofar += f.se.se;
		}
	}
 
	return can && sofar == 0;
}
 
int32_t main() {
	ios::sync_with_stdio(false); cin.tie(0);
	int T = 1;
	for(int tc = 1; tc <= T; tc++) {
		cout << (solve() ? "Yes" : "No") << endl;
	}
	return 0;
}

Measured memory_bytes: 171000