#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5;
int a[maxn];
int n;
int solve(int re){
    if(a[0]<re)return -1;
    int ans=(a[0]-re)/3;
    for(int i=1;i<n;i++){
        int t=a[i]-(3-re);
        if(t<0)re=a[i];
        else ans++,ans+=t/3,re=t%3;
    }
    return ans;
}
int main()
{
    cin>>n;
    int ans=0;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
        ans+=a[i]/3;
        a[i]=a[i]%3;
    }
    ans+=max(solve(0),max(solve(1),solve(2)));
    cout<<ans;
}


#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5;
int a[maxn];
int n;
int main()
{
        //ios::sync_with_stdio(false);
    cin>>n;
    long long ans=0;
    int cnt1=0,cnt2=0;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
        ans+=a[i]/3;
        a[i]%=3;
        if(a[i]==1)cnt1++;
        if(a[i]==2)cnt2++;
    }
    ans+=min(cnt1,cnt2);
    cnt2-=min(cnt1,cnt2);
    ans+=cnt2*2/3;
    cout<<ans;
}


#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5;
int a[maxn];
int n;
long long solve(int re){
    if(a[0]<re)return -1;
    long long ans=(a[0]-re)/3;
    for(int i=1;i<n;i++){
        int t=a[i]-(3-re);
        if(t<0)re=a[i];
        else ans++,ans+=t/3,re=t%3;
    }
    return ans;
}
int main()
{
    cin>>n;
    long long ans=0;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
    }
    ans+=max(solve(0),max(solve(1),solve(2)));
    cout<<ans;
}


#include <bits/stdc++.h>

const int MAXN = 300005;

int a[MAXN], r;

std::set<int> set[3];

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
        set[a[i] % 3].insert(i);
    }

    long long ans = 0;
    int min = 0;
    for (int i = n - 1; ~i; i--) {
        if (!a[i]) continue;
        auto it = set[a[i] % 3].end();
        --it;
        set[a[i] % 3].erase(it);

        int t = std::min(a[i] / 3, r / 3);
        ans += t;
        r -= t;
        a[i] -= t * 3;

        if (a[i] % 3 == 0) {
            ans += a[i] / 3;
            continue;
        } else if (a[i] % 3 == 1) {
            ans += a[i] / 3;
            if (a[i] == 1) continue;
            if (set[1].size() >= 2) {
                ++ans;
                int u = *set[1].begin();
                set[1].erase(set[1].begin());
                --a[u];
                u = *set[1].begin();
                set[1].erase(set[1].begin());
                --a[u];
            } else if (set[2].size()) {
                ++ans;
                int u = *set[2].begin();
                set[2].erase(set[2].begin());
                set[1].insert(u);
                a[u] -= 2;
            } else {
                r += 2;
            }
        } else {
            ans += a[i] / 3;
            if (set[1].size()) {
                ++ans;
                int u = *set[1].begin();
                set[1].erase(set[1].begin());
                --a[u];
            } else if (set[2].size()) {
                ++ans;
                int u = *set[2].begin();
                set[2].erase(set[2].begin());
                set[1].insert(u);
                --a[u];
            } else {
                ++r;
            }
        }
    }
    printf("%lld\n", ans);

    return 0;
}


#include <bits/stdc++.h>

const int MAXN = 300005;

int a[MAXN], r1, r2;

std::set<int> set[3];

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
        set[a[i] % 3].insert(i);
    }

    long long ans = 0;
    for (int i = n - 1; ~i; i--) {
        if (!a[i]) continue;
        auto it = set[a[i] % 3].end();
        --it;
        set[a[i] % 3].erase(it);

        int t = std::min(a[i] / 3, r1 / 3);
        ans += t * 3;
        r1 -= t * 3;
        a[i] -= t * 3;

        if (a[i] % 3 == 0) {
            ans += a[i] / 3;
            continue;
        } else if (a[i] % 3 == 1) {
            ans += a[i] / 3;
            if (a[i] == 1) continue;
            if (set[1].size() >= 2) {
                ++ans;
                int u = *set[1].begin();
                set[1].erase(set[1].begin());
                if (--a[u]) set[0].insert(u);
                u = *set[1].begin();
                set[1].erase(set[1].begin());
                if (--a[u]) set[0].insert(u);
            } else if (set[2].size()) {
                ++ans;
                int u = *set[2].begin();
                set[2].erase(set[2].begin());
                set[1].insert(u);
                a[u] -= 2;
            } else {
                --ans;
                ++r2;
            }
        } else {
            ans += a[i] / 3;
            if (r2) {
                ans += 2;
                --r2;
            } else if (set[1].size()) {
                ++ans;
                int u = *set[1].begin();
                set[1].erase(set[1].begin());
                if (--a[u]) set[0].insert(u);
            } else if (set[2].size()) {
                ++ans;
                int u = *set[2].begin();
                set[2].erase(set[2].begin());
                set[1].insert(u);
                --a[u];
            } else {
                ++r1;
            }
        }
    }
    printf("%lld\n", ans + r2);

    return 0;
}


#include <bits/stdc++.h>

const int MAXN = 300005;

int a[MAXN];

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) scanf("%d", &a[i]);

    long long ans = 0, rem = 0;
    for (int i = 0; i < n; i++) {
        int t = std::min(a[i] / 2ll, rem);
        a[i] -= t * 2;
        rem -= t;
        ans += t + a[i] / 3;
        a[i] %= 3;
        rem += a[i];
    }
    printf("%lld\n", ans);

    return 0;
}


#include <bits/stdc++.h>

const int MAXN = 300005;

int a[MAXN], r;

std::set<int> set[3];

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
        set[a[i] % 3].insert(i);
    }

    long long ans = 0;
    int min = 0;
    for (int i = n - 1; ~i; i--) {
        if (!a[i]) continue;
        set[a[i] % 3].erase(set[a[i] % 3].find(i));
        a[i] = std::max(0, a[i] - r / 3 * 3);
        if (a[i] % 3 == 0) {
            ans += a[i] / 3;
            continue;
        } else if (a[i] % 3 == 1) {
            ans += a[i] / 3;
            if (a[i] == 1) continue;
            if (set[1].size() >= 2) {
                ++ans;
                int u = *set[1].begin();
                set[1].erase(set[1].begin());
                --a[u];
                u = *set[1].begin();
                set[1].erase(set[1].begin());
                --a[u];
            } else if (set[2].size()) {
                ++ans;
                int u = *set[2].begin();
                set[2].erase(set[2].begin());
                set[1].insert(u);
                a[u] -= 2;
            } else {
                r += 2;
            }
        } else {
            ans += a[i] / 3;
            if (set[1].size()) {
                ++ans;
                int u = *set[1].begin();
                set[1].erase(set[1].begin());
                --a[u];
            } else if (set[2].size()) {
                ++ans;
                int u = *set[2].begin();
                set[2].erase(set[2].begin());
                set[1].insert(u);
                --a[u];
            } else {
                ++r;
            }
        }
    }
    printf("%lld\n", ans);

    return 0;
}


#include <bits/stdc++.h>

const int MAXN = 300005;

int a[MAXN], r1, r2;

std::set<int> set[3];

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
        set[a[i] % 3].insert(i);
    }

    long long ans = 0;
    for (int i = n - 1; ~i; i--) {
        if (!a[i]) continue;
        auto it = set[a[i] % 3].end();
        --it;
        set[a[i] % 3].erase(it);

        int t = std::min(a[i] / 3, r1 / 3);
        ans += t * 3;
        r1 -= t * 3;
        a[i] -= t * 3;

        if (a[i] % 3 == 0) {
            ans += a[i] / 3;
            continue;
        } else if (a[i] % 3 == 1) {
            ans += a[i] / 3;
            if (a[i] == 1) continue;
            if (r1) {
                ++ans;
                --r1;
            } else if (set[1].size() >= 2) {
                ++ans;
                int u = *set[1].begin();
                set[1].erase(set[1].begin());
                if (--a[u]) set[0].insert(u);
                u = *set[1].begin();
                set[1].erase(set[1].begin());
                if (--a[u]) set[0].insert(u);
            } else if (set[2].size()) {
                ++ans;
                int u = *set[2].begin();
                set[2].erase(set[2].begin());
                set[1].insert(u);
                a[u] -= 2;
            } else {
                --ans;
                ++r2;
            }
        } else {
            ans += a[i] / 3;
            if (r2) {
                ans += 2;
                --r2;
            } else if (set[1].size()) {
                ++ans;
                int u = *set[1].begin();
                set[1].erase(set[1].begin());
                if (--a[u]) set[0].insert(u);
            } else if (set[2].size()) {
                ++ans;
                int u = *set[2].begin();
                set[2].erase(set[2].begin());
                set[1].insert(u);
                --a[u];
            } else {
                ++r1;
            }
        }
    }
    printf("%lld\n", ans + r2);

    return 0;
}


#include <bits/stdc++.h>

const int MAXN = 300005;

int a[MAXN], r1, r2;

std::set<int> set[3];

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
        set[a[i] % 3].insert(i);
    }

    long long ans = 0;
    int min = 0;
    for (int i = n - 1; ~i; i--) {
        if (!a[i]) continue;
        auto it = set[a[i] % 3].end();
        --it;
        set[a[i] % 3].erase(it);

        int t = std::min(a[i] / 3, r1 / 3);
        ans += t;
        r1 -= t;
        a[i] -= t * 3;

        if (a[i] % 3 == 0) {
            ans += a[i] / 3;
            continue;
        } else if (a[i] % 3 == 1) {
            ans += a[i] / 3;
            if (a[i] == 1) continue;
            if (set[1].size() >= 2) {
                ++ans;
                int u = *set[1].begin();
                set[1].erase(set[1].begin());
                if (--a[u]) set[0].insert(u);
                u = *set[1].begin();
                set[1].erase(set[1].begin());
                if (--a[u]) set[0].insert(u);
            } else if (set[2].size()) {
                ++ans;
                int u = *set[2].begin();
                set[2].erase(set[2].begin());
                set[1].insert(u);
                a[u] -= 2;
            } else {
                --ans;
                ++r2;
            }
        } else {
            ans += a[i] / 3;
            if (r2) {
                ans += 2;
                --r2;
            } else if (set[1].size()) {
                ++ans;
                int u = *set[1].begin();
                set[1].erase(set[1].begin());
                if (--a[u]) set[0].insert(u);
            } else if (set[2].size()) {
                ++ans;
                int u = *set[2].begin();
                set[2].erase(set[2].begin());
                set[1].insert(u);
                --a[u];
            } else {
                ++r1;
            }
        }
    }
    printf("%lld\n", ans + r2);

    return 0;
}


#include <bits/stdc++.h>

const int MAXN = 300005;

int a[MAXN], r;

std::set<int> set[3];

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
        set[a[i] % 3].insert(i);
    }

    long long ans = 0;
    int min = 0;
    for (int i = n - 1; ~i; i--) {
        if (!a[i]) continue;
        auto it = set[a[i] % 3].end();
        --it;
        set[a[i] % 3].erase(it);

        int t = std::min(a[i] / 3, r / 3);
        ans += t;
        r -= t;
        a[i] -= t * 3;

        if (a[i] % 3 == 0) {
            ans += a[i] / 3;
            continue;
        } else if (a[i] % 3 == 1) {
            ans += a[i] / 3;
            if (a[i] == 1) continue;
            if (set[1].size() >= 2) {
                ++ans;
                int u = *set[1].begin();
                set[1].erase(set[1].begin());
                if (--a[u]) set[0].erase(set[0].begin());
                u = *set[1].begin();
                set[1].erase(set[1].begin());
                if (--a[u]) set[0].erase(set[0].begin());
            } else if (set[2].size()) {
                ++ans;
                int u = *set[2].begin();
                set[2].erase(set[2].begin());
                set[1].insert(u);
                a[u] -= 2;
            } else {
                r += 2;
            }
        } else {
            ans += a[i] / 3;
            if (set[1].size()) {
                ++ans;
                int u = *set[1].begin();
                set[1].erase(set[1].begin());
                if (--a[u]) set[0].erase(set[0].begin());
            } else if (set[2].size()) {
                ++ans;
                int u = *set[2].begin();
                set[2].erase(set[2].begin());
                set[1].insert(u);
                --a[u];
            } else {
                ++r;
            }
        }
    }
    printf("%lld\n", ans);

    return 0;
}


#include <bits/stdc++.h>

const int MAXN = 300005;

long long a[MAXN], r1, r2;

std::set<int> set[3];

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%lld", &a[i]);
        set[a[i] % 3].insert(i);
    }

    long long ans = 0;
    for (int i = n - 1; ~i; i--) {
        if (!a[i]) continue;
        auto it = set[a[i] % 3].end();
        --it;
        set[a[i] % 3].erase(it);

        long long t = std::min(a[i] / 3, r1 / 3);
        ans += t * 3;
        r1 -= t * 3;
        a[i] -= t * 3;

        if (a[i] % 3 == 0) {
            ans += a[i] / 3;
            continue;
        } else if (a[i] % 3 == 1) {
            ans += a[i] / 3;
            if (r1) {
                ++ans;
                --r1;
            } else if (a[i] == 1) {
                continue;
            } else if (set[1].size() >= 2) {
                ++ans;
                int u = *set[1].begin();
                set[1].erase(set[1].begin());
                if (--a[u]) set[0].insert(u);
                u = *set[1].begin();
                set[1].erase(set[1].begin());
                if (--a[u]) set[0].insert(u);
            } else if (set[2].size()) {
                ++ans;
                int u = *set[2].begin();
                set[2].erase(set[2].begin());
                set[1].insert(u);
                a[u] -= 2;
            } else {
                --ans;
                ++r2;
            }
        } else {
            ans += a[i] / 3;
            if (r2) {
                ans += 2;
                --r2;
            } else if (set[1].size()) {
                ++ans;
                int u = *set[1].begin();
                set[1].erase(set[1].begin());
                if (--a[u]) set[0].insert(u);
            } else if (set[2].size()) {
                ++ans;
                int u = *set[2].begin();
                set[2].erase(set[2].begin());
                set[1].insert(u);
                --a[u];
            } else {
                ++r1;
            }
        }
    }
    printf("%lld\n", ans + r2);

    return 0;
}


#include <bits/stdc++.h>

const int MAXN = 300005;

int a[MAXN], r;

std::set<int> set[3];

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
        set[a[i] % 3].insert(i);
    }

    long long ans = 0;
    int min = 0;
    for (int i = n - 1; ~i; i--) {
        if (!a[i]) continue;
        auto it = set[a[i] % 3].end();
        --it;
        set[a[i] % 3].erase(it);

        int t = std::min(a[i] / 3, r / 3);
        ans += t;
        r -= t;
        a[i] -= t * 3;

        if (a[i] % 3 == 0) {
            ans += a[i] / 3;
            continue;
        } else if (a[i] % 3 == 1) {
            ans += a[i] / 3;
            if (a[i] == 1) continue;
            if (set[1].size() >= 2) {
                ++ans;
                int u = *set[1].begin();
                set[1].erase(set[1].begin());
                if (--a[u]) set[0].insert(u);
                u = *set[1].begin();
                set[1].erase(set[1].begin());
                if (--a[u]) set[0].insert(u);
            } else if (set[2].size()) {
                ++ans;
                int u = *set[2].begin();
                set[2].erase(set[2].begin());
                set[1].insert(u);
                a[u] -= 2;
            } else {
                r += 2;
            }
        } else {
            ans += a[i] / 3;
            if (set[1].size()) {
                ++ans;
                int u = *set[1].begin();
                set[1].erase(set[1].begin());
                if (--a[u]) set[0].insert(u);
            } else if (set[2].size()) {
                ++ans;
                int u = *set[2].begin();
                set[2].erase(set[2].begin());
                set[1].insert(u);
                --a[u];
            } else {
                ++r;
            }
        }
    }
    printf("%lld\n", ans);

    return 0;
}


#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<ctime>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
#include<functional>
#include<sstream>

#define MAX 222222222
#define mod 1073741824
#define max(a,b) (a)<(b)?(b):(a)
#define min(a,b) (a)>(b)?(b):(a)

typedef long long ll;
typedef unsigned long long ull;

using namespace std;

typedef long long ll;
typedef pair<ll, ll> Point;
typedef pair<int, int> Time;
const ll Mod = 1000000007;
const double PI = acos(-1.0);
const double eps = 1e-9;
//ll Mod;

inline int sign(double a)
{
	return a < -eps ? -1 : a > eps;
}

ll gcd(ll a, ll b)
{
	return b ? gcd(b, a%b) : a;
}

ll ex_gcd(ll a, ll b, ll &x, ll &y)
{
	if (b > a)
		return ex_gcd(b, a, y, x);
	if (b == 0)
	{
		x = 1;
		y = 0;
		return a;
	}
	else
	{
		ll x1, y1;
		ll res = ex_gcd(b, a%b, x1, y1);
		x = y1;
		y = x1 - a / b * y1;
		return res;
	}
}

ll lcm(ll a, ll b)
{
	return a  / gcd(a, b)* b;
}

ll ksc(ll x, ll y)
{
	ll res = 0;
	while (y)
	{
		if (y & 1)
			res = (res + x) % Mod;
		x = (x + x) % Mod;
		y >>= 1;
	}
	return res % Mod;
}

ll Pow(ll n, ll x)
{
	ll res = 1;
	while (x)
	{
		if (x & 1)
			res = ksc(res, n);
		n = ksc(n, n);
		x >>= 1;
	}
	return res;
}

ll inv(ll i,ll mmod)
{
	if (i == 1)
		return 1;
	return (mmod - mmod / i)*inv(mmod%i, mmod) % mmod;
}

ll num[300010];

int main()
{
	int n;
	cin >> n;
	ll ans = 0;
	for (int i = 0; i < n; i++)
	{
		cin >> num[i];
		ans += (num[i] / 3);
		num[i] %= 3;
	}
	ll one_num = 0;
	for (int i = 0; i < n; i++)
	{
		if (num[i] == 2)
		{
			if (one_num > 0)
			{
				ans++;
				one_num--;
			}
			else
				one_num += 2;
		}
		else
			one_num += num[i];
	}
	cout << ans;
	return 0;
}
// 3 8

/*
NEAT
AI
JUROR
*/

/**
*　　┏┓　　　┏┓+ +
*　┏┛┻━━━┛┻┓ + +
*　┃　　　　　　　┃
*　┃　　　━　　　┃ ++ + + +
*  ████━████+
*  ◥██◤　◥██◤ +
*　┃　　　┻　　　┃
*　┃　　　　　　　┃ + +
*　┗━┓　　　┏━┛
*　　　┃　　　┃ + + + +Code is far away from
*　　　┃　　　┃ + bug with the animal protecting
*　　　┃　 　 ┗━━━┓ 神兽保佑,代码无bug
*　　　┃ 　　　　　　 ┣┓
*　　  ┃ 　　　　　 　┏┛
*　    ┗┓┓┏━┳┓┏┛ + + + +
*　　　　┃┫┫　┃┫┫
*　　　　┗┻┛　┗┻┛+ + + +
*/


#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<ctime>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
#include<functional>
#include<sstream>

#define MAX 222222222
#define mod 1073741824
#define max(a,b) (a)<(b)?(b):(a)
#define min(a,b) (a)>(b)?(b):(a)

typedef long long ll;
typedef unsigned long long ull;

using namespace std;

typedef long long ll;
typedef pair<ll, ll> Point;
typedef pair<int, int> Time;
const ll Mod = 1000000007;
const double PI = acos(-1.0);
const double eps = 1e-9;
//ll Mod;

inline int sign(double a)
{
	return a < -eps ? -1 : a > eps;
}

ll gcd(ll a, ll b)
{
	return b ? gcd(b, a%b) : a;
}

ll ex_gcd(ll a, ll b, ll &x, ll &y)
{
	if (b > a)
		return ex_gcd(b, a, y, x);
	if (b == 0)
	{
		x = 1;
		y = 0;
		return a;
	}
	else
	{
		ll x1, y1;
		ll res = ex_gcd(b, a%b, x1, y1);
		x = y1;
		y = x1 - a / b * y1;
		return res;
	}
}

ll lcm(ll a, ll b)
{
	return a  / gcd(a, b)* b;
}

ll ksc(ll x, ll y)
{
	ll res = 0;
	while (y)
	{
		if (y & 1)
			res = (res + x) % Mod;
		x = (x + x) % Mod;
		y >>= 1;
	}
	return res % Mod;
}

ll Pow(ll n, ll x)
{
	ll res = 1;
	while (x)
	{
		if (x & 1)
			res = ksc(res, n);
		n = ksc(n, n);
		x >>= 1;
	}
	return res;
}

ll inv(ll i,ll mmod)
{
	if (i == 1)
		return 1;
	return (mmod - mmod / i)*inv(mmod%i, mmod) % mmod;
}
/*
ll num[300010], num1[300010];

int main()
{
	int n;
	cin >> n;
	ll ans = 0;
	for (int i = 0; i < n; i++)
	{
		cin >> num[i];
		num1[i] = num[i];
	}
	ll one_num = num[0];
	for (int i = 1; i < n; i++)
	{
		int x = min(num[i] / 2, one_num);
		int y = num[i] - x * 2;
		ans += (x + y / 3);
		if (num[0] - x >= 0)
			num[0] -= x;
		one_num -= x;
		one_num += (y % 3);
	}
	ans += (num[0] / 3);

	ll ans1 = 0;
	for (int i = 0; i < n; i++)
	{
		ans1 += (num1[i] / 3);
		num1[i] %= 3;
	}
	ll one_num1 = 0;
	for (int i = 0; i < n; i++)
	{
		if (num1[i] == 2)
		{
			if (one_num1 > 0)
			{
				ans1++;
				one_num1--;
			}
			else
				one_num1 += 2;
		}
		else
			one_num1 += num1[i];
	}
	cout << (max(ans1, ans));
	return 0;
}

*/

ll num[300010];

int main()
{
	int n;
	cin >> n;
	ll ans = 0;
	for (int i = 0; i < n; i++)
	{
		cin >> num[i];
	}
	ans += (num[0] / 3);
	num[0] %= 3;
	ll one_num = num[0];
	for (int i = 1; i < n; i++)
	{
		int x = min(num[i] / 2, one_num);
		int y = num[i] - x * 2;
		ans += (x + y / 3);
		one_num -= x;
		one_num += (y % 3);
	}
	cout << ans;
	return 0;
}
// 3 8

/*
NEAT
AI
JUROR
*/

/**
*　　┏┓　　　┏┓+ +
*　┏┛┻━━━┛┻┓ + +
*　┃　　　　　　　┃
*　┃　　　━　　　┃ ++ + + +
*  ████━████+
*  ◥██◤　◥██◤ +
*　┃　　　┻　　　┃
*　┃　　　　　　　┃ + +
*　┗━┓　　　┏━┛
*　　　┃　　　┃ + + + +Code is far away from
*　　　┃　　　┃ + bug with the animal protecting
*　　　┃　 　 ┗━━━┓ 神兽保佑,代码无bug
*　　　┃ 　　　　　　 ┣┓
*　　  ┃ 　　　　　 　┏┛
*　    ┗┓┓┏━┳┓┏┛ + + + +
*　　　　┃┫┫　┃┫┫
*　　　　┗┻┛　┗┻┛+ + + +
*/


#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<ctime>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
#include<functional>
#include<sstream>

#define MAX 222222222
#define mod 1073741824
#define max(a,b) (a)<(b)?(b):(a)
#define min(a,b) (a)>(b)?(b):(a)

typedef long long ll;
typedef unsigned long long ull;

using namespace std;

typedef long long ll;
typedef pair<ll, ll> Point;
typedef pair<int, int> Time;
const ll Mod = 1000000007;
const double PI = acos(-1.0);
const double eps = 1e-9;
//ll Mod;

inline int sign(double a)
{
	return a < -eps ? -1 : a > eps;
}

ll gcd(ll a, ll b)
{
	return b ? gcd(b, a%b) : a;
}

ll ex_gcd(ll a, ll b, ll &x, ll &y)
{
	if (b > a)
		return ex_gcd(b, a, y, x);
	if (b == 0)
	{
		x = 1;
		y = 0;
		return a;
	}
	else
	{
		ll x1, y1;
		ll res = ex_gcd(b, a%b, x1, y1);
		x = y1;
		y = x1 - a / b * y1;
		return res;
	}
}

ll lcm(ll a, ll b)
{
	return a  / gcd(a, b)* b;
}

ll ksc(ll x, ll y)
{
	ll res = 0;
	while (y)
	{
		if (y & 1)
			res = (res + x) % Mod;
		x = (x + x) % Mod;
		y >>= 1;
	}
	return res % Mod;
}

ll Pow(ll n, ll x)
{
	ll res = 1;
	while (x)
	{
		if (x & 1)
			res = ksc(res, n);
		n = ksc(n, n);
		x >>= 1;
	}
	return res;
}

ll inv(ll i,ll mmod)
{
	if (i == 1)
		return 1;
	return (mmod - mmod / i)*inv(mmod%i, mmod) % mmod;
}

ll num[300010];

int main()
{
	int n;
	cin >> n;
	ll ans = 0;
	for (int i = 0; i < n; i++)
	{
		cin >> num[i];
	}
	ans += (num[0] / 3);
	num[0] %= 3;
	ll one_num = num[0];
	for (int i = 0; i < n; i++)
	{
		int x = min(num[i] / 2, one_num);
		int y = num[i] - x * 2;
		ans += (x + y / 3);
		one_num -= x;
		one_num += (y % 3);
	}
	cout << ans;
	return 0;
}
// 3 8

/*
NEAT
AI
JUROR
*/

/**
*　　┏┓　　　┏┓+ +
*　┏┛┻━━━┛┻┓ + +
*　┃　　　　　　　┃
*　┃　　　━　　　┃ ++ + + +
*  ████━████+
*  ◥██◤　◥██◤ +
*　┃　　　┻　　　┃
*　┃　　　　　　　┃ + +
*　┗━┓　　　┏━┛
*　　　┃　　　┃ + + + +Code is far away from
*　　　┃　　　┃ + bug with the animal protecting
*　　　┃　 　 ┗━━━┓ 神兽保佑,代码无bug
*　　　┃ 　　　　　　 ┣┓
*　　  ┃ 　　　　　 　┏┛
*　    ┗┓┓┏━┳┓┏┛ + + + +
*　　　　┃┫┫　┃┫┫
*　　　　┗┻┛　┗┻┛+ + + +
*/


#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<ctime>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
#include<functional>
#include<sstream>

#define MAX 222222222
#define mod 1073741824
#define max(a,b) (a)<(b)?(b):(a)
#define min(a,b) (a)>(b)?(b):(a)

typedef long long ll;
typedef unsigned long long ull;

using namespace std;

typedef long long ll;
typedef pair<ll, ll> Point;
typedef pair<int, int> Time;
const ll Mod = 1000000007;
const double PI = acos(-1.0);
const double eps = 1e-9;
//ll Mod;

inline int sign(double a)
{
	return a < -eps ? -1 : a > eps;
}

ll gcd(ll a, ll b)
{
	return b ? gcd(b, a%b) : a;
}

ll ex_gcd(ll a, ll b, ll &x, ll &y)
{
	if (b > a)
		return ex_gcd(b, a, y, x);
	if (b == 0)
	{
		x = 1;
		y = 0;
		return a;
	}
	else
	{
		ll x1, y1;
		ll res = ex_gcd(b, a%b, x1, y1);
		x = y1;
		y = x1 - a / b * y1;
		return res;
	}
}

ll lcm(ll a, ll b)
{
	return a  / gcd(a, b)* b;
}

ll ksc(ll x, ll y)
{
	ll res = 0;
	while (y)
	{
		if (y & 1)
			res = (res + x) % Mod;
		x = (x + x) % Mod;
		y >>= 1;
	}
	return res % Mod;
}

ll Pow(ll n, ll x)
{
	ll res = 1;
	while (x)
	{
		if (x & 1)
			res = ksc(res, n);
		n = ksc(n, n);
		x >>= 1;
	}
	return res;
}

ll inv(ll i,ll mmod)
{
	if (i == 1)
		return 1;
	return (mmod - mmod / i)*inv(mmod%i, mmod) % mmod;
}

ll num[300010], num1[300010];

int main()
{
	int n;
	cin >> n;
	ll ans = 0;
	for (int i = 0; i < n; i++)
	{
		cin >> num[i];
		num1[i] = num[i];
	}
	ll one_num = num[0];
	for (int i = 1; i < n; i++)
	{
		int x = min(num[i] / 2, one_num);
		int y = num[i] - x * 2;
		ans += (x + y / 3);
		if (num[0] - x >= 0)
			num[0] -= x;
		one_num -= x;
		one_num += (y % 3);
	}
	ans += (num[0] / 3);

	ll ans1 = 0;
	for (int i = 0; i < n; i++)
	{
		ans1 += (num1[i] / 3);
		num1[i] %= 3;
	}
	ll one_num1 = 0;
	for (int i = 0; i < n; i++)
	{
		if (num1[i] == 2)
		{
			if (one_num1 > 0)
			{
				ans1++;
				one_num1--;
			}
			else
				one_num1 += 2;
		}
		else
			one_num1 += num1[i];
	}
	cout << (max(ans1, ans));
	return 0;
}
// 3 8

/*
NEAT
AI
JUROR
*/

/**
*　　┏┓　　　┏┓+ +
*　┏┛┻━━━┛┻┓ + +
*　┃　　　　　　　┃
*　┃　　　━　　　┃ ++ + + +
*  ████━████+
*  ◥██◤　◥██◤ +
*　┃　　　┻　　　┃
*　┃　　　　　　　┃ + +
*　┗━┓　　　┏━┛
*　　　┃　　　┃ + + + +Code is far away from
*　　　┃　　　┃ + bug with the animal protecting
*　　　┃　 　 ┗━━━┓ 神兽保佑,代码无bug
*　　　┃ 　　　　　　 ┣┓
*　　  ┃ 　　　　　 　┏┛
*　    ┗┓┓┏━┳┓┏┛ + + + +
*　　　　┃┫┫　┃┫┫
*　　　　┗┻┛　┗┻┛+ + + +
*/


#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<ctime>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
#include<functional>
#include<sstream>

#define MAX 222222222
#define mod 1073741824
#define max(a,b) (a)<(b)?(b):(a)
#define min(a,b) (a)>(b)?(b):(a)

typedef long long ll;
typedef unsigned long long ull;

using namespace std;

typedef long long ll;
typedef pair<ll, ll> Point;
typedef pair<int, int> Time;
const ll Mod = 1000000007;
const double PI = acos(-1.0);
const double eps = 1e-9;
//ll Mod;

inline int sign(double a)
{
	return a < -eps ? -1 : a > eps;
}

ll gcd(ll a, ll b)
{
	return b ? gcd(b, a%b) : a;
}

ll ex_gcd(ll a, ll b, ll &x, ll &y)
{
	if (b > a)
		return ex_gcd(b, a, y, x);
	if (b == 0)
	{
		x = 1;
		y = 0;
		return a;
	}
	else
	{
		ll x1, y1;
		ll res = ex_gcd(b, a%b, x1, y1);
		x = y1;
		y = x1 - a / b * y1;
		return res;
	}
}

ll lcm(ll a, ll b)
{
	return a  / gcd(a, b)* b;
}

ll ksc(ll x, ll y)
{
	ll res = 0;
	while (y)
	{
		if (y & 1)
			res = (res + x) % Mod;
		x = (x + x) % Mod;
		y >>= 1;
	}
	return res % Mod;
}

ll Pow(ll n, ll x)
{
	ll res = 1;
	while (x)
	{
		if (x & 1)
			res = ksc(res, n);
		n = ksc(n, n);
		x >>= 1;
	}
	return res;
}

ll inv(ll i,ll mmod)
{
	if (i == 1)
		return 1;
	return (mmod - mmod / i)*inv(mmod%i, mmod) % mmod;
}

int num[300010];

int main()
{
	int n;
	cin >> n;
	ll ans = 0;
	for (int i = 0; i < n; i++)
	{
		cin >> num[i];
		ans += (num[i] / 3);
		num[i] %= 3;
	}
	int one_num = 0;
	for (int i = 0; i < n; i++)
	{
		if (num[i] == 2)
		{
			if (one_num > 0)
			{
				ans++;
				one_num--;
			}
			else
				one_num += 2;
		}
		else
			one_num += num[i];
	}
	cout << ans;
	return 0;
}

/*
NEAT
AI
JUROR
*/

/**
*　　┏┓　　　┏┓+ +
*　┏┛┻━━━┛┻┓ + +
*　┃　　　　　　　┃
*　┃　　　━　　　┃ ++ + + +
*  ████━████+
*  ◥██◤　◥██◤ +
*　┃　　　┻　　　┃
*　┃　　　　　　　┃ + +
*　┗━┓　　　┏━┛
*　　　┃　　　┃ + + + +Code is far away from
*　　　┃　　　┃ + bug with the animal protecting
*　　　┃　 　 ┗━━━┓ 神兽保佑,代码无bug
*　　　┃ 　　　　　　 ┣┓
*　　  ┃ 　　　　　 　┏┛
*　    ┗┓┓┏━┳┓┏┛ + + + +
*　　　　┃┫┫　┃┫┫
*　　　　┗┻┛　┗┻┛+ + + +
*/


#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<ctime>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
#include<functional>
#include<sstream>

#define MAX 222222222
#define mod 1073741824
#define max(a,b) (a)<(b)?(b):(a)
#define min(a,b) (a)>(b)?(b):(a)

typedef long long ll;
typedef unsigned long long ull;

using namespace std;

typedef long long ll;
typedef pair<ll, ll> Point;
typedef pair<int, int> Time;
const ll Mod = 1000000007;
const double PI = acos(-1.0);
const double eps = 1e-9;
//ll Mod;

inline int sign(double a)
{
	return a < -eps ? -1 : a > eps;
}

ll gcd(ll a, ll b)
{
	return b ? gcd(b, a%b) : a;
}

ll ex_gcd(ll a, ll b, ll &x, ll &y)
{
	if (b > a)
		return ex_gcd(b, a, y, x);
	if (b == 0)
	{
		x = 1;
		y = 0;
		return a;
	}
	else
	{
		ll x1, y1;
		ll res = ex_gcd(b, a%b, x1, y1);
		x = y1;
		y = x1 - a / b * y1;
		return res;
	}
}

ll lcm(ll a, ll b)
{
	return a  / gcd(a, b)* b;
}

ll ksc(ll x, ll y)
{
	ll res = 0;
	while (y)
	{
		if (y & 1)
			res = (res + x) % Mod;
		x = (x + x) % Mod;
		y >>= 1;
	}
	return res % Mod;
}

ll Pow(ll n, ll x)
{
	ll res = 1;
	while (x)
	{
		if (x & 1)
			res = ksc(res, n);
		n = ksc(n, n);
		x >>= 1;
	}
	return res;
}

ll inv(ll i,ll mmod)
{
	if (i == 1)
		return 1;
	return (mmod - mmod / i)*inv(mmod%i, mmod) % mmod;
}

ll num[300010];

int main()
{
	int n;
	cin >> n;
	ll ans = 0;
	for (int i = 0; i < n; i++)
	{
		cin >> num[i];
	}
	ll one_num = num[0];
	for (int i = 1; i < n; i++)
	{
		int x = min(num[i] / 2, one_num);
		int y = num[i] - x * 2;
		ans += (x + y / 3);
		one_num -= x;
		one_num += (y % 3);
	}
	ans += (num[0] / 3);
	cout << ans;
	return 0;
}
// 3 8

/*
NEAT
AI
JUROR
*/

/**
*　　┏┓　　　┏┓+ +
*　┏┛┻━━━┛┻┓ + +
*　┃　　　　　　　┃
*　┃　　　━　　　┃ ++ + + +
*  ████━████+
*  ◥██◤　◥██◤ +
*　┃　　　┻　　　┃
*　┃　　　　　　　┃ + +
*　┗━┓　　　┏━┛
*　　　┃　　　┃ + + + +Code is far away from
*　　　┃　　　┃ + bug with the animal protecting
*　　　┃　 　 ┗━━━┓ 神兽保佑,代码无bug
*　　　┃ 　　　　　　 ┣┓
*　　  ┃ 　　　　　 　┏┛
*　    ┗┓┓┏━┳┓┏┛ + + + +
*　　　　┃┫┫　┃┫┫
*　　　　┗┻┛　┗┻┛+ + + +
*/


#include<bits/stdc++.h>
using namespace std;
#define		N		300005

int Rest[N],a[N],b[N],t;
int n;
long long ans,tmp;

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
	for (int i=1;i<=n;i++)
    {
        tmp+=1ll*a[i]/2;
        if (a[i]&1&&tmp) tmp--,ans++;
    }
    ans+=1ll*2*tmp/3;
	printf("%lld\n",ans);
}


#include<bits/stdc++.h>
using namespace std;
#define		N		300005

int Rest[N],a[N],b[N],t;
int n;
long long ans;

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        ans+=1ll*a[i]/3;
        a[i]%=3;
    }
	for (int i=1;i<=n;i++)
    {
        if (a[i]==1) t++;
        else if (a[i]==2) if (t) ans++,t--;
        else t+=2;
    }
	printf("%lld\n",ans);
}


#include<bits/stdc++.h>
using namespace std;
#define		N		300005

int Rest[N],a[N],b[N],t;
int n;
long long ans;

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	for (int i=1;i<=n;i++)
	{
		if (a[i]&1) Rest[++t]=i;
		a[i]/=2;
		b[i]=a[i]*2;
	}
	int l=1;
	for (int i=n;i;i--) if (a[i])
	{
		while (Rest[t]>i) t--;
		if (t>=a[i])
		{
			ans+=1ll*a[i];
			t-=a[i];
			a[i]=0;
		}
		else {
			ans+=1ll*t;
			a[i]-=t;
			t=0;
		}
		while (a[i])
		{
			if (i<l) break;
			if (i==l)
			{
				ans+=1ll*b[i]/3;
				a[i]=0;
			}
			else
			if (b[l]>a[i])
			{
				ans+=1ll*a[i];
				a[i]=0;
				b[l]-=a[i];
			}
			else {
				ans+=b[l];
				a[i]-=b[l];
				l++;
			}
		}
	}
	printf("%lld\n",ans);
}


#include<bits/stdc++.h>
using namespace std;
#define		N		300005

int Rest[N],a[N],b[N],t;
int n;
long long ans;

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	for (int i=1;i<=n;i++)
	{
		if (a[i]&1) Rest[++t]=i;
		a[i]/=2;
		b[i]=a[i]*2;
	}
	int l=1;
	for (int i=n;i;i--) if (a[i])
	{
		while (Rest[t]>i) t--;
		if (t>=a[i])
		{
			ans+=1ll*a[i];
			t-=a[i];
			a[i]=0;
			b[i]=0;
		}
		else {
			ans+=1ll*t;
			a[i]-=t;
			b[i]-=t*2;
			t=0;
		}
		while (a[i])
		{
			if (i<l) break;
			if (i==l)
			{
				ans+=1ll*b[i]/3;
				a[i]=0;
			}
			else
			if (b[l]>a[i])
			{
				ans+=1ll*a[i];
				a[i]=0;
				b[l]-=a[i];
			}
			else {
				ans+=b[l];
				a[i]-=b[l];
				l++;
			}
		}
	}
	printf("%lld\n",ans);
}


#include<bits/stdc++.h>
using namespace std;
#define		N		300005

int Rest[N],a[N],b[N],t;
int n;
long long ans;

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        ans+=1ll*a[i]/3;
        a[i]%=3;
    }
	for (int i=1;i<=n;i++)
	{
		if (a[i]&1) Rest[++t]=i;
		a[i]/=2;
		b[i]=a[i]*2;
	}
	int l=1;
	for (int i=n;i;i--) if (a[i])
	{
		while (Rest[t]>i) t--;
		if (t>=a[i])
		{
			ans+=1ll*a[i];
			t-=a[i];
			a[i]=0;
			b[i]=0;
		}
		else {
			ans+=1ll*t;
			a[i]-=t;
			b[i]-=t*2;
			t=0;
		}
		while (a[i])
		{
			if (i<l) break;
			if (i==l)
			{
				ans+=1ll*b[i]/3;
				a[i]=0;
			}
			else
			if (b[l]>a[i])
			{
				ans+=1ll*a[i];
				a[i]=0;
				b[l]-=a[i];
			}
			else {
				ans+=1ll*b[l];
				a[i]-=b[l];
				b[l]=0;
				l++;
			}
		}
	}
	printf("%lld\n",ans);
}


#include<bits/stdc++.h>
using namespace std;
#define		N		300005

int Rest[N],a[N],b[N],t;
int n;
long long ans,tmp;

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
	for (int i=n;i;i--)
    {
        tmp+=1ll*a[i]/2;
        if ((a[i]&1)&&tmp) tmp--,ans++;
    }
    ans+=1ll*2*tmp/3;
	printf("%lld\n",ans);
}


#include<bits/stdc++.h>
using namespace std;
#define		N		300005

int Rest[N],a[N],b[N],t;
int n;
long long ans;

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	for (int i=1;i<=n;i++)
	{
		if (a[i]&1) Rest[++t]=i;
		a[i]/=2;
		b[i]=a[i]*2;
	}
	int l=1;
	for (int i=n;i;i--) if (a[i])
	{
		while (Rest[t]>i) t--;
		if (t>=a[i])
		{
			ans+=1ll*a[i];
			t-=a[i];
			a[i]=0;
			b[i]=0;
		}
		else {
			ans+=1ll*t;
			a[i]-=t;
			b[i]-=t*2;
			t=0;
		}
		while (a[i])
		{
			if (i<l) break;
			if (i==l)
			{
				ans+=1ll*b[i]/3;
				a[i]=0;
			}
			else
			if (b[l]>a[i])
			{
				ans+=1ll*a[i];
				a[i]=0;
				b[l]-=a[i];
			}
			else {
				ans+=1ll*b[l];
				a[i]-=b[l];
				b[l]=0;
				l++;
			}
		}
	}
	printf("%lld\n",ans);
}


#include<cstdio>
#include<algorithm>
using namespace std;
const int N=3e5+5;
int a[N];
int num[N];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%d",a+i);
    int ans=0;
    for(int i=0,j=1;i<n&&j<=n;i++)
    {
        if(i==j)
            j++;
        ans+=a[i]/3;
        a[i]%=3;
        int tmp=0;
        while(j<n&&tmp<a[i])
        {
            int x=a[j]/2;
            if(tmp+x>a[i])
                a[j]-=(a[i]-tmp)*2,tmp=a[i];
            else
                tmp+=x,a[j]%=2,++j;
        }
        ans+=tmp;
    }
    printf("%d\n",ans);
    return 0;
}


#include<cstdio>
#include<algorithm>
using namespace std;
const int N=3e5+5;
int a[N];
int num[N];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%d",a+i);
    long long ans=0;
    for(int i=0,j=1;i<n&&j<=n;i++)
    {
        if(i==j)
            j++;
        ans+=a[i]/3;
        a[i]%=3;
        int tmp=0;
        while(j<n&&tmp<a[i])
        {
            int x=a[j]/2;
            if(tmp+x>a[i])
                a[j]-=(a[i]-tmp)<<1,tmp=a[i];
            else
                tmp+=x,a[j]&=1,++j;
        }
        ans+=tmp;
    }
    printf("%lld\n",ans);
    return 0;
}


#include<cstdio>
#include<algorithm>
using namespace std;
const int N=3e5+5;
int a[N];
int num[N];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%d",a+i);
    long long ans=0;
    for(int i=0,j=1;i<n&&j<=n;i++)
    {
        if(i==j)
            j++;
        ans+=a[i]/3;
        a[i]%=3;
        int tmp=0;
        while(j<n&&tmp<a[i])
        {
            int x=a[j]/2;
            if(tmp+x>a[i])
                a[j]-=(a[i]-tmp)*2,tmp=a[i];
            else
                tmp+=x,a[j]%=2,++j;
        }
        ans+=tmp;
    }
    printf("%lld\n",ans);
    return 0;
}


#include<cstdio>
#include<algorithm>
using namespace std;
const int N=3e5+5;
int a[N];
int num[N];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%d",a+i);
    int ans=0;
    for(int i=0,j=1;i<n&&j<n;i++)
    {
        if(i==j)
            j++;
        ans+=a[i]/3;
        a[i]%=3;
        int tmp=0;
        while(j<n&&tmp<a[i])
        {
            int x=a[j]/2;
            if(tmp+x>a[i])
                a[j]-=(a[i]-tmp)*2,tmp=a[i];
            else
                tmp+=x,a[j]%=2,++j;
        }
        ans+=tmp;
    }
    printf("%d\n",ans);
    return 0;
}


#include<cstdio>
#include<algorithm>
using namespace std;
const int N=3e5+5;
int a[N];
int num[N];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%d",a+i);
    long long ans=0;
    for(int i=0,j=1;i<n&&j<=n;i++)
    {
        if(i==j)
            j++;
        ans+=a[i]/3;
        a[i]%=3;
        long long tmp=0;
        while(j<n&&tmp<a[i])
        {
            int x=a[j]/2;
            if(tmp+x>a[i])
                a[j]-=(a[i]-tmp)*2ll,tmp=a[i];
            else
                tmp+=x,a[j]%=2,++j;
        }
        ans+=tmp;
    }
    printf("%lld\n",ans);
    return 0;
}


#include<iostream>
#include<vector>
#include<set>
#include<algorithm>
using namespace std;

int main(){
    // ios::sync_with_stdio(false);
    // cin.tie(0);
    int n;
    vector<int> len;
    vector<int> one, two;
    cin >> n;

    len.resize(n);

    for(int i=0; i<n; i++){
        cin >> len[i];
    }

    long long int ans = 0;
    for(int i=0; i<n; i++){
        ans += len[i]/3;
        len[i] = len[i]%3;
        int ptr = i+1;
        while( len[i] > 0 && ptr < n){
            while( ptr < n && len[ptr] % 3 != 2 ) ptr++;
            if( ptr < n ){
                len[ptr] -= 2;
                len[i]--;
                ans++;
            }
        }
    }
    cout << ans << '\n';

    return 0;
}


#include<iostream>
#include<vector>
#include<set>
#include<algorithm>
using namespace std;

int main(){
    // ios::sync_with_stdio(false);
    // cin.tie(0);
    int n;
    vector<int> len;
    vector<int> one, two;
    cin >> n;

    len.resize(n);

    for(int i=0; i<n; i++){
        cin >> len[i];
    }

    long long int ans = 0;
    int ptr = 0;
    for(int i=0; i<n; i++){
        ans += len[i]/3;
        len[i] = len[i]%3;
        ptr = max( i+1, ptr );
        while( len[i] > 0 && ptr < n){
            while( ptr < n && len[ptr] % 3 == 0 ) ptr++;
            if( ptr == n ) break;
            if( ptr < n ){
                len[ptr] -= 2;
                len[i]--;
                ans++;
            }
        }
    }
    cout << ans << '\n';

    return 0;
}


#include<iostream>
#include<vector>
#include<set>
#include<algorithm>
using namespace std;

int main(){
    // ios::sync_with_stdio(false);
    // cin.tie(0);
    int n;
    vector<int> len;
    vector<int> one, two;
    cin >> n;

    len.resize(n);

    for(int i=0; i<n; i++){
        cin >> len[i];
    }

    long long int ans = 0;
    int ptr = 0;
    for(int i=0; i<n; i++){
        ans += len[i]/3;
        len[i] = len[i]%3;
        ptr = max( i+1, ptr );
        while( len[i] > 0 && ptr < n){
            while( ptr < n && len[ptr] < 2 ) ptr++;
            if( ptr == n ) break;
            len[ptr] -= 2;
            len[i]--;
            ans++;
        }
    }
    cout << ans << '\n';

    return 0;
}


#include<iostream>
#include<vector>
#include<set>
#include<algorithm>
using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    vector<int> len;
    cin >> n;

    len.resize(n);

    for(int i=0; i<n; i++){
        cin >> len[i];
    }

    long long int ans = 0;
    int ptr = 0;
    for(int i=0; i<n; i++){
        ans += len[i]/3;
        len[i] = len[i]%3;
        ptr = max( i+1, ptr );
        while( len[i] > 0 && ptr < n){
            while( ptr < n && len[ptr] < 2 ) ptr++;
            if( ptr == n ) break;
            len[ptr] -= 2;
            len[i]--;
            ans++;
        }
    }
    cout << ans << '\n';

    return 0;
}


#include<iostream>
#include<vector>
#include<set>
#include<algorithm>
using namespace std;

int main(){
    // ios::sync_with_stdio(false);
    // cin.tie(0);
    int n;
    vector<int> len;
    vector<int> one, two;
    cin >> n;

    len.resize(n);

    for(int i=0; i<n; i++){
        cin >> len[i];
    }

    long long int ans = 0;
    int ptr = 0;
    for(int i=0; i<n; i++){
        ans += len[i]/3;
        len[i] = len[i]%3;
        ptr = max( i+1, ptr );
        while( len[i] > 0 && ptr < n){
            while( ptr < n && len[ptr] % 3 != 2 ) ptr++;
            if( ptr == n ) break;
            if( ptr < n ){
                len[ptr] -= 2;
                len[i]--;
                ans++;
            }
        }
    }
    cout << ans << '\n';

    return 0;
}


#include<cstdio>
#include<algorithm>
#define nn 300005
using namespace std;
typedef long long ll;
long long  n,a[nn],ans,j;
int main(){
    scanf("%I64d",&n);
    for (ll i=1;i<=n;i++)scanf("%I64d",&a[i]);
    j=1;
    for (ll i=n;i>=1;i--){
    if (a[i]>=3)ans+=a[i]/3,a[i]%=3;

    while (a[i]>1){
    while (a[j]<=0&&j<=i)j++;
    if (j>i)break;
    a[i]-=2;a[j]--;ans++;
    }
    }

    printf("%I64d\n",ans);
    //for(;;);
    return 0;
}


#include<cstdio>
#include<algorithm>
#define nn 300005
using namespace std;
typedef long long ll;
long long  n,a[nn],ans,t;
int main(){
    scanf("%I64d",&n);
    for (ll i=1;i<=n;i++)scanf("%I64d",&a[i]);
    t=0;
    for (ll i=1;i<=n;i++){
        while (t&&a[i]>=2)a[i]-=2,ans++,t--;
        ans+=a[i]/3;a[i]%=3;
        t+=a[i];
    }
    printf("%I64d\n",ans);
    //for(;;);
    return 0;
}


import sys
import random, math
from collections import defaultdict

n,m = [int(__) for __ in raw_input().split()]
dw = {'m':[0] * m,'n':[0] * n}
#arr = [int(__) for __ in raw_input().split()]
dt = defaultdict(set)
ds = defaultdict(set)
de = defaultdict(set)
for j in range(n):
    st = raw_input()
    for i in range(m):
        if st[i] == '>':
            dt[('n',j)].add(('m',i))
            ds[('m',i)].add(('n',j))
        elif st[i] == '<':
            ds[('n',j)].add(('m',i))
            dt[('m',i)].add(('n',j))
        else:
            de[('n',j)].add(('m',i))
            de[('m',i)].add(('n',j))
lv = set()
for k in ds:
    if k not in dt:
        lv.add(k)
sv = set()
cur = 1
#print(dt,ds,de)
while len(lv):
    newlv = set()
    for k in lv:
        sv.add(k)
        if dw[k[0]][k[1]] > 0 and dw[k[0]][k[1]] != 0:
            print('NO')
            exit()
        dw[k[0]][k[1]] = cur
        for targ in ds[k]:
            dt[targ].remove(k)
            if len(dt[targ]) == 0:
                del(dt[targ])
                newlv.add(targ)
        del(ds[k])
    lv = newlv
    cur += 1
for k in de:
    for el in de[k]:
        if dw[k[0]][k[1]] > 0 and dw[el[0]][el[1]] > 0 and dw[k[0]][k[1]] != dw[el[0]][el[1]]:
            print('NO')
            exit()
        if dw[k[0]][k[1]] > 0:
            dw[el[0]][el[1]] = dw[k[0]][k[1]]
        if dw[el[0]][el[1]] > 0:
            dw[k[0]][k[1]] = dw[el[0]][el[1]]
#print(sv)
if 0 not in dw['m'] and 0 not in dw['n']:
    print('YES')
    print(' '.join([str(x) for x in dw['n']]))
    print(' '.join([str(x) for x in dw['m']]))
else:
    print('NO')


import sys,os,io
range = xrange

# INPUT
input_data = os.read(0,os.fstat(0).st_size)
input = io.BytesIO(input_data).readline

n,m = [int(x) for x in input().split()]
data = [input() for _ in range(n)]

rep = [-1]*(n+m)
for root in range(n+m):
    if rep[root]>=0:continue

    rep[root] = root

    Q = [root]
    while Q:
        node = Q.pop()

        if node<n:
            i = node
            for j in range(m):
                if rep[j+n]==-1 and data[i][j]=='=':
                    rep[j+n] = root
                    Q.append(j+n)
        else:
            j = node-n
            for i in range(n):
                if rep[i]==-1 and data[i][j]=='=':
                    rep[i] = root
                    Q.append(i)

better = [[] for _ in range(n+m)]
for i in range(n):
    for j in range(m):
        if data[i][j]=='>':
            better[rep[j+n]].append(rep[i])
        elif data[i][j]=='<':
            better[rep[i]].append(rep[j+n])

counter = [0]*(n+m)
for ind in range(n+m):
    if rep[ind]==ind:
        for ind2 in better[ind]:
            counter[ind2] += 1

val = [-1]*(n+m)
leaves = [ind for ind in range(n+m) if rep[ind]==ind and counter[ind]==0]
for ind in leaves:
    val[ind] = 1
while leaves:
    ind = leaves.pop()
    v = val[ind]
    for ind2 in better[ind]:
        val[ind2] = max(val[ind2],v+1)
        counter[ind2] -= 1
        if counter[ind2]==0:
            leaves.append(ind2)

val = [val[rep[i]] for i in range(n+m)]
if any(v<0 for v in val):
    print 'No'
else:
    print 'YES'
    print ' '.join(str(x) for x in val[:n])
    print ' '.join(str(x) for x in val[n:n+m])


import sys,os,io
range = xrange

# INPUT
input_data = os.read(0,os.fstat(0).st_size)
input = io.BytesIO(input_data).readline

n,m = [int(x) for x in input().split()]
data = [input() for _ in range(n)]

rep = [-1]*(n+m)
for root in range(n+m):
    if rep[root]>=0:continue

    rep[root] = root

    Q = [root]
    while Q:
        node = Q.pop()

        if node<n:
            i = node
            for j in range(m):
                if rep[j+n]==-1 and data[i][j]=='=':
                    rep[j+n] = root
                    Q.append(j+n)
        else:
            j = node-n
            for i in range(n):
                if rep[i]==-1 and data[i][j]=='=':
                    rep[i] = root
                    Q.append(i)
assert(not any(r<0 for r in rep))

better = [[] for _ in range(n+m)]
for i in range(n):
    for j in range(m):
        if data[i][j]=='>':
            better[rep[j+n]].append(rep[i])
        elif data[i][j]=='<':
            better[rep[i]].append(rep[j+n])

counter = [0]*(n+m)
for ind in range(n+m):
    if rep[ind]==ind:
        for ind2 in better[ind]:
            counter[ind2] += 1

val = [-1]*(n+m)
found = sum(1 for ind in range(n+m) if rep[ind]==ind)

leaves = [ind for ind in range(n+m) if rep[ind]==ind and counter[ind]==0]
for ind in leaves:
    val[ind] = 1
while leaves:
    ind = leaves.pop()
    found -= 1
    v = val[ind]
    for ind2 in better[ind]:
        if ind2==ind:
            print 'No'
            sys.exit()
        val[ind2] = max(val[ind2],v+1)
        counter[ind2] -= 1
        if counter[ind2]==0:
            leaves.append(ind2)

val = [val[rep[i]] for i in range(n+m)]
if found>0 or any(v<0 for v in val):
    print 'No'
else:
    print 'Yes'
    print ' '.join(str(x) for x in val[:n])
    print ' '.join(str(x) for x in val[n:n+m])


import sys,os,io
range = xrange

# INPUT
input_data = os.read(0,os.fstat(0).st_size)
input = io.BytesIO(input_data).readline

n,m = [int(x) for x in input().split()]
data = [input() for _ in range(n)]

rep = [-1]*(n+m)
for root in range(n+m):
    if rep[root]>=0:continue

    rep[root] = root

    Q = [root]
    while Q:
        node = Q.pop()

        if node<n:
            i = node
            for j in range(m):
                if rep[j+n]==-1 and data[i][j]=='=':
                    rep[j+n] = root
                    Q.append(j+n)
        else:
            j = node-n
            for i in range(n):
                if rep[i]==-1 and data[i][j]=='=':
                    rep[i] = root
                    Q.append(i)
assert(not any(r<0 for r in rep))

better = [[] for _ in range(n+m)]
for i in range(n):
    for j in range(m):
        if data[i][j]=='>':
            better[rep[j+n]].append(rep[i])
        elif data[i][j]=='<':
            better[rep[i]].append(rep[j+n])

counter = [0]*(n+m)
for ind in range(n+m):
    if rep[ind]==ind:
        for ind2 in better[ind]:
            counter[ind2] += 1

val = [-1]*(n+m)
leaves = [ind for ind in range(n+m) if rep[ind]==ind and counter[ind]==0]
for ind in leaves:
    val[ind] = 1
while leaves:
    ind = leaves.pop()
    v = val[ind]
    for ind2 in better[ind]:
        if ind2==ind:
            print 'No'
            sys.exit()
        val[ind2] = max(val[ind2],v+1)
        counter[ind2] -= 1
        if counter[ind2]==0:
            leaves.append(ind2)

val = [val[rep[i]] for i in range(n+m)]
if any(v<0 for v in val):
    print 'No'
else:
    print 'Yes'
    print ' '.join(str(x) for x in val[:n])
    print ' '.join(str(x) for x in val[n:n+m])


#!/usr/bin/env pypy
import collections
#import random
import heapq
import bisect
import math
import time

class Solution2:
    def solve(self, A1, A2):
        pass

def gcd(a, b):
    if not b: return a
    return gcd(b, a%b)
def lcm(a, b):
    return b*a//gcd(b,a)

class Solution:

    def solve(self, grid):
        def union(i,j):
            leader_i, leader_j = find(i), find(j)
            sets[leader_j] = sets[i] = sets[j] = leader_i
        def find(i):
            while i != sets[i]:
                i = sets[i]
            return i
        N = len(grid) + len(grid[0])
        sets = list(range(N))
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j] == '=': union(i,j+len(grid))

        graph = collections.defaultdict(set)
        inc = collections.defaultdict(set)
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                leader_i, leader_j = find(i), find(j+len(grid))
                if grid[i][j] == '>':
                    if leader_i == leader_j:
                        print("No")
                        return
                    graph[leader_j].add(leader_i)
                    inc[leader_i].add(leader_j)
                elif grid[i][j] == '<':
                    if leader_i == leader_j:
                        print("No")
                        return
                    graph[leader_i].add(leader_j)
                    inc[leader_j].add(leader_i)

        self.levels = [0]*N
        def dfs(node, level):
            self.levels[node] = max(self.levels[node],level)
            if not inc[node]:
                seen.add(node)
                for next_node in graph[node]:
                    inc[next_node].discard(node)
                    dfs(next_node,self.levels[node]+1)

        seen = set()
        for i in range(N):
            l = find(i)
            if not inc[l] and l not in seen:
                seen.add(l)
                dfs(l, 1)

        if any(inc[find(node)] for node in range(N)):
            print("No")
            return

        for i in range(N):
            l = find(i)
            if l != i:
                self.levels[i] = self.levels[l]

        print("Yes")
        print(' '.join(str(o) for o in self.levels[:len(grid)]))
        print(' '.join(str(o) for o in self.levels[len(grid):]))

sol = Solution()
sol2 = Solution2()

#TT = int(input())
for test_case in range(1):
    N, M = input().split()
    a = []
    for _ in range(int(N)):
        a.append(input())
    #b = [int(c) for c in input().split()]

    out = sol.solve(a)
    #print(' '.join([str(o) for o in out]))
    #print(str(out))

    # out2 = sol2.solve(s)

# for _ in range(100000):
#     rand = [random.randrange(60) for _ in range(10)]
#     out1 = sol.solve(rand)
#     out2 = sol2.solve(rand)
#     if out1 != out2:
#         print(rand, out1, out2)
#         break


'''input
3 3
>>>
<<<
>>>
'''
#print(input().split())
n, m = list(map(int, input().split()))
#exit(0)
g = []
for i in range(n):
	g += [input()]
# print(g)

memo = {}
def dfs(u):
	if u not in memo:
		memo[u] = 0
		res = 1
		if u < n:
			for v in range(m):
				if g[u][v] == '>':
					res = max(res, dfs(n + v) + 1)
			for v in range(m):
				if g[u][v] == '=':
					res = max(res, dfs(n + v))
		else:
			for v in range(n):
				if g[v][u - n] == '<':
					res = max(res, dfs(v) + 1)
			for v in range(n):
				if g[v][u - n] == '=':
					res = max(res, dfs(v))
		memo[u] = res
	return memo[u]
ans = [0] * (n + m)
for i in range(n + m):
	ans[i] = dfs(i)
for i in range(n):
	for j in range(m):
		if g[i][j] == '=' and ans[i] != ans[n + j]:
			print("No")
			exit(0)
		if g[i][j] == '<' and ans[i] >= ans[n + j]:
			print("No")
			exit(0)
		if g[i][j] == '>' and ans[i] <= ans[n + j]:
			print("No")
			exit(0)
print("Yes")
print(*ans[:n])
print(*ans[n:])


'''input
3 3
>>>
<<<
>>>
'''
#print(input().split())
n, m = list(map(int, input().split()))
#exit(0)
g = []
for i in range(n):
	g += [input()]
# print(g)

memo = {}

def dfs(u):
	if u not in memo:
		memo[u] = res = 1
		if u < n:
			for v in range(m):
				if g[u][v] == '>':
					res = max(res, dfs(n + v) + 1)
			for v in range(m):
				if g[u][v] == '=':
					res = max(res, dfs(n + v))
			for v in range(m):
				if g[u][v] == '=':
					memo[n + v] = max(memo[n + v], res)
		else:
			for v in range(n):
				if g[v][u - n] == '<':
					res = max(res, dfs(v) + 1)
			for v in range(n):
				if g[v][u - n] == '=':
					res = max(res, dfs(v))
			for v in range(n):
				if g[v][u - n] == '=':
					memo[v] = max(memo[v], res)
		memo[u] = res
	return memo[u]
ans = [0] * (n + m)
for i in range(n + m):
	ans[i] = dfs(i)
for i in range(n):
	for j in range(m):
		if g[i][j] == '=' and ans[i] != ans[n + j]:
			print("No")
			exit(0)
		if g[i][j] == '<' and ans[i] >= ans[n + j]:
			print("No")
			exit(0)
		if g[i][j] == '>' and ans[i] <= ans[n + j]:
			print("No")
			exit(0)
print("Yes")
print(*ans[:n])
print(*ans[n:])


'''input
3 3
>>>
<<<
>>>
'''
#print(input().split())
n, m = list(map(int, input().split()))
#exit(0)
g = []
for i in range(n):
	g += [input()]
# print(g)

memo = {}
def dfs(u):
	if u not in memo:
		memo[u] = res = 1
		if u < n:
			for v in range(m):
				if g[u][v] == '>':
					res = max(res, dfs(n + v) + 1)
			for v in range(m):
				if g[u][v] == '=':
					res = max(res, dfs(n + v))
		else:
			for v in range(n):
				if g[v][u - n] == '<':
					res = max(res, dfs(v) + 1)
			for v in range(n):
				if g[v][u - n] == '=':
					res = max(res, dfs(v))
		memo[u] = res
	return memo[u]
ans = [0] * (n + m)
for i in range(n + m):
	ans[i] = dfs(i)
for i in range(n):
	for j in range(m):
		if g[i][j] == '=' and ans[i] != ans[n + j]:
			print("No")
			exit(0)
		if g[i][j] == '<' and ans[i] >= ans[n + j]:
			print("No")
			exit(0)
		if g[i][j] == '>' and ans[i] <= ans[n + j]:
			print("No")
			exit(0)
print("Yes")
print(*ans[:n])
print(*ans[n:])


n, m = (int(t) for t in input().split(' '))
def direction(c):
    if c == '=': return 0
    if c == '>': return 1
    return -1

mx = [[direction(c) for c in input()] for _ in range(n)]

index = 0
class DSet(object):
    def __init__(self, value):
        global index
        self.values = set([value])
        self.index = index
        index += 1
        self.edges_to = set()
        self.edges_from = set()

    def __len__(self):
        return len(self.values)
    def update(self, other_dset):
        self.values.update(other_dset.values)

    def add_edge_to(self, i):
        self.edges_to.add(i)

    def add_edge_from(self, i):
        self.edges_from.add(i)

    def remove_edge_from(self, i):
        self.edges_from.remove(i)

dsu = [DSet(i) for i in range(n+m)]

def union(v1, v2):
    if len(dsu[v1]) > len(dsu[v2]):
        d = dsu[v1]
        d.update(dsu[v2])
    else:
        d = dsu[v2]
        d.update(dsu[v1])

    dsu[v1] = dsu[v2] = d

for i in range(n):
    for j in range(m):
        if not mx[i][j]:
            union(i, n + j)

for i in range(n):
    for j in range(m):
        if mx[i][j] > 0:
            dsu[i].add_edge_from(dsu[n + j].index)
            dsu[n+j].add_edge_to(dsu[i].index)
        elif mx[i][j] < 0:
            dsu[n + j].add_edge_from(dsu[i].index)
            dsu[i].add_edge_to(dsu[n+j].index)

weights = [1] * (n+m)
dsu_by_index = { d.index: d for d in dsu }

while True:
    try:
        v = next(d for d in range(n+m) if not len(dsu[d].edges_from) and len(dsu[d].edges_to))
    except:
        break

    dsuv = dsu[v]
    for edge in dsu[v].edges_to:
        dsue = dsu_by_index[edge]
        dsue.remove_edge_from(dsuv.index)
        for value in dsue.values:
            weights[value] = weights[v] + 1

    dsu[v].edges_to.clear()

ok = all(not len(d.edges_from) and not len(d.edges_to) for d in dsu)
if ok:
    print('Yes')
    print(*weights[0:n])
    print(*weights[n:])
else:
    print('No')


n, m = (int(t) for t in input().split(' '))
def direction(c):
    if c == '=': return 0
    if c == '>': return 1
    return -1

mx = [[direction(c) for c in input()] for _ in range(n)]

class DSet(object):
    def __init__(self, value):
        self.values = set([value])
        self.edges_to = set()
        self.edges_from = set()

    def __len__(self):
        return len(self.values)
    def update(self, other_dset):
        self.values.update(other_dset.values)

    def add_edge_to(self, i):
        self.edges_to.add(i)

    def add_edge_from(self, i):
        self.edges_from.add(i)

    def remove_edge_from(self, i):
        self.edges_from.remove(i)

dsu = [DSet(i) for i in range(n+m)]

def union(v1, v2):
    if len(dsu[v1]) > len(dsu[v2]):
        d = dsu[v1]
        d.update(dsu[v2])
    else:
        d = dsu[v2]
        d.update(dsu[v1])

    dsu[v1] = dsu[v2] = d

for i in range(n):
    for j in range(m):
        if not mx[i][j]:
            union(i, n + j)

for i in range(n):
    for j in range(m):
        if mx[i][j] > 0:
            dsu[i].add_edge_from(n + j)
            dsu[n+j].add_edge_to(i)
        elif mx[i][j] < 0:
            dsu[n + j].add_edge_from(i)
            dsu[i].add_edge_to(n+j)

weights = [1] * (n+m)

while True:
    try:
        v = next(d for d in range(n+m) if not len(dsu[d].edges_from) and len(dsu[d].edges_to))
    except:
        break

    for edge in dsu[v].edges_to:
        dsu[edge].remove_edge_from(v)
        weights[edge] = weights[v] + 1

    dsu[v].edges_to.clear()

ok = all(not len(d.edges_from) and not len(d.edges_to) for d in dsu)
if ok:
    print('Yes')
    print(*weights[0:n])
    print(*weights[n:])
else:
    print('No')


import sys
from collections import deque
sys.setrecursionlimit(10**6)
readline = sys.stdin.readline

N, M = map(int, readline().split())
L = [readline().strip() for i in range(N)]

def root(x):
    if x == p[x]:
        return x
    p[x] = y = root(p[x])
    return y
def unite(x, y):
    px = root(x); py = root(y)
    if px < py:
        p[py] = px
    else:
        p[px] = py

*p, = range(N+M)
for i in range(N):
    for j, c in enumerate(L[i]):
        if c == '=':
            unite(i, N+j)

G = [[] for i in range(N+M)]
RG = [[] for i in range(N+M)]
deg = [0]*(N+M)

for i in range(N):
    x = root(i)
    for j, c in enumerate(L[i]):
        y = root(N+j)
        if c == '<':
            G[x].append(y)
            RG[y].append(x)
            deg[y] += 1
        elif c == '>':
            G[y].append(x)
            RG[x].append(y)
            deg[x] += 1
A = [-1]*(N+M)

que = deque()
for i in range(N+M):
    if root(i) == i and deg[i] == 0:
        que.append(i)
while que:
    v = que.popleft()
    lb = 0
    for w in RG[v]:
        lb = max(lb, A[w])
    A[v] = lb+1
    for w in G[v]:
        deg[w] -= 1
        if deg[w] == 0:
            que.append(w)
if any(root(i) == i and A[i] == -1 for i in range(N+M)):
    print("No")
else:
    for i in range(N+M):
        A[i] = A[root(i)]
    print("Yes")
    print(*A[:N])
    print(*A[N:])


import sys
import collections

def main():
    input = iter(sys.stdin)
    N, M = map(int, next(input).split())

    dsu = DSU(N + M)
    edges = []
    for i in range(N):
        line = next(input).strip()
        for j, sign in enumerate(line):
            j = j + N
            if sign == '=':
                dsu.merge(i, j)
            else:
                edges.append((i, j) if sign == '<' else (j, i))

    incoming = collections.defaultdict(set)
    outgoing = collections.defaultdict(set)
    nodes = [dsu.root(i) for i in range(N + M)]
    for i, j in edges:
        incoming[dsu.root(i)].add(dsu.root(j))
        outgoing[dsu.root(j)].add(dsu.root(i))

    no_incoming = list(set(i for i in nodes if i not in incoming))
    ordering = []
    while no_incoming:
        node = no_incoming.pop(-1)
        ordering.append(node)
        for n in outgoing[node]:
            incoming[n].discard(node)
            if not incoming[n]:
                no_incoming.append(n)
    if len(ordering) != len(nodes):
        return 'NO'

    root_to_index = {}
    for n in reversed(ordering):
        if not outgoing[n]:
            root_to_index[n] = 0
        else:
            root_to_index[n] = max(root_to_index[x] for x in outgoing[n]) + 1

    s = ['YES']
    s.append(' '.join(str(root_to_index[dsu.root(i)] + 1) for i in range(N)))
    s.append(' '.join(str(root_to_index[dsu.root(i + N)] + 1) for i in range(M)))
    return '\n'.join(s)

class DSU():

    def __init__(self, n):
        self.parents = [i for i in range(n)]

    def root(self, i):
        try:
            if self.parents[i] != i:
                self.parents[i] = self.root(self.parents[i])
        except:
            print(i, len(self.parents))
            raise
        return self.parents[i]

    def merge(self, i, j):
        r1 = self.root(i)
        r2 = self.root(j)
        if r1 != r2:
            self.parents[r1] = r2

if __name__ == '__main__':
    print(main())


import sys
import collections

def main():
    input = iter(sys.stdin)
    N, M = map(int, next(input).split())

    dsu = DSU(N + M)
    edges = []
    for i in range(N):
        line = next(input).strip()
        for j, sign in enumerate(line):
            j = j + N
            if sign == '=':
                dsu.merge(i, j)
            else:
                edges.append((i, j) if sign == '<' else (j, i))

    incoming = collections.defaultdict(set)
    outgoing = collections.defaultdict(set)
    nodes = sorted(set([dsu.root(i) for i in range(N + M)]))
    for i, j in edges:
        incoming[dsu.root(i)].add(dsu.root(j))
        outgoing[dsu.root(j)].add(dsu.root(i))

    no_incoming = list(set(i for i in nodes if i not in incoming))
    ordering = []
    while no_incoming:
        node = no_incoming.pop(-1)
        ordering.append(node)
        for n in outgoing[node]:
            incoming[n].discard(node)
            if not incoming[n]:
                no_incoming.append(n)
    if len(ordering) != len(nodes):
        return 'NO'

    root_to_index = {}
    for n in reversed(ordering):
        if not outgoing[n]:
            root_to_index[n] = 0
        else:
            root_to_index[n] = max(root_to_index[x] for x in outgoing[n]) + 1

    s = ['YES']
    s.append(' '.join(str(root_to_index[dsu.root(i)] + 1) for i in range(N)))
    s.append(' '.join(str(root_to_index[dsu.root(i + N)] + 1) for i in range(M)))
    return '\n'.join(s)

class DSU():

    def __init__(self, n):
        self.parents = [i for i in range(n)]

    def root(self, i):
        try:
            if self.parents[i] != i:
                self.parents[i] = self.root(self.parents[i])
        except:
            print(i, len(self.parents))
            raise
        return self.parents[i]

    def merge(self, i, j):
        r1 = self.root(i)
        r2 = self.root(j)
        if r1 != r2:
            self.parents[r1] = r2

if __name__ == '__main__':
    print(main())


import sys
import collections

def main():
    input = iter(sys.stdin)
    N, M = map(int, next(input).split())

    dsu = DSU(N + M)
    edges = []
    for i in range(N):
        line = next(input).strip()
        for j, sign in enumerate(line):
            j = j + N
            if sign == '=':
                dsu.merge(i, j)
            else:
                edges.append((i, j) if sign == '<' else (j, i))

    incoming = collections.defaultdict(set)
    outgoing = collections.defaultdict(set)
    nodes = [dsu.root(i) for i in range(N + M)]
    for i, j in edges:
        incoming[dsu.root(i)].add(dsu.root(j))
        outgoing[dsu.root(j)].add(dsu.root(i))

    no_incoming = list(set(i for i in nodes if i not in incoming))
    ordering = []
    while no_incoming:
        node = no_incoming.pop(-1)
        ordering.append(node)
        for n in outgoing[node]:
            incoming[n].discard(node)
            if not incoming[n]:
                no_incoming.append(n)
    if len(ordering) != len(nodes):
        return 'NO'

    ordering = list(reversed(ordering))
    root_to_index = {}
    max = 0
    for i in range(len(ordering)):
        if i != 0 and (ordering[i] < N) != (ordering[i - 1] < N):
            max += 1
        root_to_index[ordering[i]] = max

    s = ['YES']
    s.append(' '.join(str(root_to_index[dsu.root(i)] + 1) for i in range(N)))
    s.append(' '.join(str(root_to_index[dsu.root(i + N)] + 1) for i in range(M)))
    return '\n'.join(s)

class DSU():

    def __init__(self, n):
        self.parents = [i for i in range(n)]

    def root(self, i):
        try:
            if self.parents[i] != i:
                self.parents[i] = self.root(self.parents[i])
        except:
            print(i, len(self.parents))
            raise
        return self.parents[i]

    def merge(self, i, j):
        r1 = self.root(i)
        r2 = self.root(j)
        if r1 != r2:
            self.parents[r1] = r2

if __name__ == '__main__':
    print(main())


class UnionFind:

    def __init__(self, n):
        self.follow = [-1]*(n+1)
        self.num_follower = [1]*(n+1)

    def root_index_of(self, a):
        r = a
        while self.follow[r] > -1:
                 r = self.follow[r]
        return r

    def connected(self, a, b):
        return self.root_index_of(a) == self.root_index_of(b)

    def connect(self, a, b):
        ra = self.root_index_of(a)
        rb = self.root_index_of(b)

        if ra == rb:
            return

        if self.num_follower[ra] < self.num_follower[rb]:
            self.follow[ra] = rb
            self.follow[a] = rb
            self.num_follower[rb] += self.num_follower[ra]
        else:
            self.follow[rb] = ra
            self.follow[b] = ra
            self.num_follower[ra] += self.num_follower[rb]

import sys
from collections import deque
input = sys.stdin.readline
n, m = map(int, input().split())
s = [input() for _ in range(n)]

uf = UnionFind(n+m)
v = [[] for _ in range(n+m)]
c = [0]*(n+m)
for i in range(n):
    for j in range(m):
        if s[i][j] == "=":
            uf.connect(i, j+n)

for i in range(n):
    for j in range(m):
        if s[i][j] == "<":
            v[uf.root_index_of(i)].append(uf.root_index_of(n+j))
            c[uf.root_index_of(n+j)] += 1
        elif s[i][j] == ">":
            v[uf.root_index_of(n+j)].append(uf.root_index_of(i))
            c[uf.root_index_of(i)] += 1

ans = [0]*(n+m)
used = [0]*(n+m)
que = deque([])
for i in range(n+m):
    if c[i] == 0:
        ans[i] = 1
        used[i] = 1
        que.append(i)

while que:
    x = que.popleft()
    for i in v[x]:
        if used[i]:
            continue
        ans[i] = max(ans[x]+1, ans[i])
        c[i] -= 1
        if c[i] == 0:
            used[i] = 1
            que.append(i)

for i in range(n+m):
    if used[i] == 0:
        print("No")
        exit()

for i in range(n):
    for j in range(m):
        if s[i][j] == "=":
            if ans[uf.root_index_of(i)] != ans[uf.root_index_of(n+j)]:
                print("No")
                exit()

for i in range(n):
    for j in range(m):
        if s[i][j] == "<":
            if ans[uf.root_index_of(i)] >= ans[uf.root_index_of(n+j)]:
                print("No")
                exit()

for i in range(n):
    for j in range(m):
        if s[i][j] == ">":
            if ans[uf.root_index_of(i)] <= ans[uf.root_index_of(n+j)]:
                print("No")
                exit()

print("Yes")
for i in range(n):
    print(ans[uf.root_index_of(i)], end=" ")
print()
for j in range(m):
    print(ans[uf.root_index_of(j+n)], end=" ")
print()


class mergefind:
    def __init__(self,n):
        self.parent = list(range(n))
        self.size = [1]*n
        self.num_sets = n

    def find(self,a):
        to_update = []

        while a != self.parent[a]:
            to_update.append(a)
            a = self.parent[a]

        for b in to_update:
            self.parent[b] = a

        return self.parent[a]

    def merge(self,a,b):
        a = self.find(a)
        b = self.find(b)

        if a==b:
            return

        if self.size[a]<self.size[b]:
            a,b = b,a

        self.num_sets -= 1
        self.parent[b] = a
        self.size[a] += self.size[b]

    def set_size(self, a):
        return self.size[self.find(a)]

    def __len__(self):
        return self.num_sets

def toposort(C, n):
    indeg = [0]*n
    for i,neighs in enumerate(C):
        for neigh in neighs:
            indeg[neigh] += 1

    S = [i for i in range(n) if indeg[i] == 0]
    nparent = indeg[:]
    topo = []
    while S:
        cur = S.pop()
        topo.append(cur)
        for neigh in C[cur]:
            nparent[neigh] -= 1
            if nparent[neigh] == 0:
                S.append(neigh)
#         nparent[cur] = -1
    return topo

n,m = map(int,input().split())
A = [input() for _ in range(n)]

mf = mergefind(n+m)

# merge equal elements
for i in range(n):
    for j in range(m):
        if A[i][j] == '=':
            mf.merge(i,n+j)

# Connections: smaller -> larger
C = [set() for _ in range(n+m)]
for i in range(n):
    for j in range(m):
        if A[i][j] == '<':
            C[mf.find(i)].add(mf.find(n+j))
        elif A[i][j] == '>':
            C[mf.find(n+j)].add(mf.find(i))

# Walk through graph in toposort order
# What I'm pointing to must be at least
# my value + 1
D = [1]*(n+m)
for cur in toposort(C, n+m):
    for neigh in C[cur]:
        D[neigh] = max(D[neigh], D[cur]+1)

# Propagate values within equal clusters
D = [D[mf.find(i)] for i in range(n+m)]

# Validate answer
ok = True
for i in range(n):
    for j in range(m):
        if A[i][j] == '<':
            if D[i] >= D[n+j]:
                ok = False
        elif A[i][j] == '>':
            if D[i] <= D[n+j]:
                ok = False
        else:
            if D[i] != D[n+j]:
                ok = False

if ok:
    print('Yes')
    print(*D[:n])
    print(*D[n:])
else:
    print('No')


class mergefind:
    def __init__(self,n):
        self.parent = list(range(n))
        self.size = [1]*n
        self.num_sets = n

    def find(self,a):
        to_update = []

        while a != self.parent[a]:
            to_update.append(a)
            a = self.parent[a]

        for b in to_update:
            self.parent[b] = a

        return self.parent[a]

    def merge(self,a,b):
        a = self.find(a)
        b = self.find(b)

        if a==b:
            return

        if self.size[a]<self.size[b]:
            a,b = b,a

        self.num_sets -= 1
        self.parent[b] = a
        self.size[a] += self.size[b]

    def set_size(self, a):
        return self.size[self.find(a)]

    def __len__(self):
        return self.num_sets

def toposort(C, n):
    indeg = [0]*n
    for i,neighs in enumerate(C):
        for neigh in neighs:
            indeg[neigh] += 1

    S = [i for i in range(n) if indeg[i] == 0]
    nparent = indeg[:]
    topo = []
    while S:
        cur = S.pop()
        topo.append(cur)
        for neigh in C[cur]:
            nparent[neigh] -= 1
            if nparent[neigh] == 0:
                S.append(neigh)
        nparent[cur] = -1
    return topo

n,m = map(int,input().split())
A = [input() for _ in range(n)]

mf = mergefind(n+m)

# merge equal elements
for i in range(n):
    for j in range(m):
        if A[i][j] == '=':
            mf.merge(i,n+j)

# Connections: smaller -> larger
C = [set() for _ in range(n+m)]
for i in range(n):
    for j in range(m):
        if A[i][j] == '<':
            C[mf.find(i)].add(mf.find(n+j))
        elif A[i][j] == '>':
            C[mf.find(n+j)].add(mf.find(i))

# Walk through graph in toposort order
# What I'm pointing to must be at least
# my value + 1
D = [1]*(n+m)
for cur in toposort(C, n+m):
    for neigh in C[cur]:
        D[neigh] = max(D[neigh], D[cur]+1)

# Propagate values within equal clusters
D = [D[mf.find(i)] for i in range(n+m)]

# Validate answer
ok = True
for i in range(n):
    for j in range(m):
        if A[i][j] == '<':
            if D[i] >= D[n+j]:
                ok = False
        elif A[i][j] == '>':
            if D[i] <= D[n+j]:
                ok = False
        else:
            if D[i] != D[n+j]:
                ok = False

if ok:
    print('Yes')
    print(*D[:n])
    print(*D[n:])
else:
    print('No')


class mergefind:
    def __init__(self,n):
        self.parent = list(range(n))
        self.size = [1]*n
        self.num_sets = n

    def find(self,a):
        to_update = []

        while a != self.parent[a]:
            to_update.append(a)
            a = self.parent[a]

        for b in to_update:
            self.parent[b] = a

        return self.parent[a]

    def merge(self,a,b):
        a = self.find(a)
        b = self.find(b)

        if a==b:
            return

        if self.size[a]<self.size[b]:
            a,b = b,a

        self.num_sets -= 1
        self.parent[b] = a
        self.size[a] += self.size[b]

    def set_size(self, a):
        return self.size[self.find(a)]

    def __len__(self):
        return self.num_sets

def toposort(C, n):
    indeg = [0]*n
    for i,neighs in enumerate(C):
        for neigh in neighs:
            indeg[neigh] += 1

    S = [i for i in range(n) if indeg[i] == 0]
    nparent = indeg[:]
    topo = []
    while S:
        cur = S.pop()
        topo.append(cur)
        for neigh in C[cur]:
            nparent[neigh] -= 1
            if nparent[neigh] == 0:
                S.append(neigh)
        nparent[cur] = -1
    return topo

n,m = map(int,input().split())
A = [input() for _ in range(n)]

mf = mergefind(n+m)

# merge equal elements
for i in range(n):
    for j in range(m):
        if A[i][j] == '=':
            mf.merge(i,n+j)

# Connections: smaller -> larger
C = [set() for _ in range(n+m)]
for i in range(n):
    for j in range(m):
        if A[i][j] == '<':
            C[i].add(mf.find(n+j))
        elif A[i][j] == '>':
            C[n+j].add(mf.find(i))

# Walk through graph in toposort order
# What I'm pointing to must be at least
# my value + 1
D = [1]*(n+m)
for cur in toposort(C, n+m):
    for neigh in C[cur]:
        D[mf.find(neigh)] = max(D[mf.find(neigh)], D[mf.find(cur)]+1)

# Propagate values within equal clusters
D = [D[mf.find(i)] for i in range(n+m)]

# Validate answer
ok = True
for i in range(n):
    for j in range(m):
        if A[i][j] == '<':
            if D[i] >= D[n+j]:
                ok = False
        elif A[i][j] == '>':
            if D[i] <= D[n+j]:
                ok = False
        else:
            if D[i] != D[n+j]:
                ok = False

if ok:
    print('Yes')
    print(*D[:n])
    print(*D[n:])
else:
    print('No')


class mergefind:
    def __init__(self,n):
        self.parent = list(range(n))
        self.size = [1]*n
        self.num_sets = n

    def find(self,a):
        to_update = []

        while a != self.parent[a]:
            to_update.append(a)
            a = self.parent[a]

        for b in to_update:
            self.parent[b] = a

        return self.parent[a]

    def merge(self,a,b):
        a = self.find(a)
        b = self.find(b)

        if a==b:
            return

        if self.size[a]<self.size[b]:
            a,b = b,a

        self.num_sets -= 1
        self.parent[b] = a
        self.size[a] += self.size[b]

    def set_size(self, a):
        return self.size[self.find(a)]

    def __len__(self):
        return self.num_sets

def toposort(C, n):
    indeg = [0]*n
    for i,neighs in enumerate(C):
        for neigh in neighs:
            indeg[neigh] += 1

    S = [i for i in range(n) if indeg[i] == 0]
    nparent = indeg[:]
    topo = []
    while S:
        cur = S.pop()
        topo.append(cur)
        for neigh in C[cur]:
            nparent[neigh] -= 1
            if nparent[neigh] == 0:
                S.append(neigh)
        nparent[cur] = -1
    return topo

n,m = map(int,input().split())
A = [input() for _ in range(n)]

mf = mergefind(n+m)

# merge equal elements
for i in range(n):
    for j in range(m):
        if A[i][j] == '=':
            mf.merge(i,n+j)

# Connections: smaller -> larger
C = [set() for _ in range(n+m)]
for i in range(n):
    for j in range(m):
        if A[i][j] == '<':
            C[i].add(mf.find(n+j))
        elif A[i][j] == '>':
            C[n+j].add(mf.find(i))

# Walk through graph in toposort order
# What I'm pointing to must be at least
# my value + 1
D = [1]*(n+m)
for cur in toposort(C, n+m):
    for neigh in C[cur]:
        D[neigh] = max(D[neigh], D[mf.find(cur)]+1)

# Propagate values within equal clusters
D = [D[mf.find(i)] for i in range(n+m)]

# Validate answer
ok = True
for i in range(n):
    for j in range(m):
        if A[i][j] == '<':
            if D[i] >= D[n+j]:
                ok = False
        elif A[i][j] == '>':
            if D[i] <= D[n+j]:
                ok = False
        else:
            if D[i] != D[n+j]:
                ok = False

if ok:
    print('Yes')
    print(*D[:n])
    print(*D[n:])
else:
    print('No')


n,m = map(int,input().split())
A = [input() for _ in range(n)]

class mergefind:
    def __init__(self,n):
        self.parent = list(range(n))
        self.size = [1]*n
        self.num_sets = n

    def find(self,a):
        to_update = []

        while a != self.parent[a]:
            to_update.append(a)
            a = self.parent[a]

        for b in to_update:
            self.parent[b] = a

        return self.parent[a]

    def merge(self,a,b):
        a = self.find(a)
        b = self.find(b)

        if a==b:
            return

        if self.size[a]<self.size[b]:
            a,b = b,a

        self.num_sets -= 1
        self.parent[b] = a
        self.size[a] += self.size[b]

    def set_size(self, a):
        return self.size[self.find(a)]

    def __len__(self):
        return self.num_sets

mf = mergefind(n+m)

for i in range(n):
    for j in range(m):
        if A[i][j] == '=':
            mf.merge(i,n+j)

C = [set() for _ in range(n+m)]

for i in range(n):
    for j in range(m):
        if A[i][j] == '<':
            C[i].add(mf.find(n+j))
        elif A[i][j] == '>':
            C[n+j].add(mf.find(i))

def toposort(C, n):
    indeg = [0]*n
    for i,neighs in enumerate(C):
        for neigh in neighs:
            indeg[neigh] += 1

    S = [i for i in range(n) if indeg[i] == 0]
    nparent = indeg[:]
    topo = []
    while S:
        cur = S.pop()
        topo.append(cur)
        for neigh in C[cur]:
            nparent[neigh] -= 1
            if nparent[neigh] == 0:
                S.append(neigh)
        nparent[cur] = -1
    return topo

D = [1]*(n+m)
for cur in toposort(C, n+m):
    for neigh in C[cur]:
        D[neigh] = max(D[neigh], D[cur]+1)

D = [D[mf.find(i)] for i in range(n+m)]

ok = True
for i in range(n):
    for j in range(m):
        if A[i][j] == '<':
            if D[i] >= D[n+j]:
                ok = False
        elif A[i][j] == '>':
            if D[i] <= D[n+j]:
                ok = False
        else:
            if D[i] != D[n+j]:
                ok = False

if ok:
    print('Yes')
    print(*D[:n])
    print(*D[n:])
else:
    print('No')


import os
from io import BytesIO
#input = BytesIO(os.read(0, os.fstat(0).st_size)).readline

from itertools import product

class CYCLE(Exception):
    ...

debug_print = lambda *args: None

def solve(n, m, gs):
    def union(u, v):
        p, q = find(u), find(v)
        if p == q: return
        if rank[p] < rank[q]: p, q = q, p
        if rank[p] == rank[q]: rank[p] += 1
        parent[q] = p

    def find(u):
        if parent[u] == u: return u
        else:
            p = find(parent[u])
            parent[u] = p
            return p

    rank = [0] * (n + m)
    parent = list(range(n + m))

    for i, j in product(range(n), range(m)):
        if gs[i][j] == "=":
            union(i, n + j)

    vertices = set(parent)
    v_sz = len(vertices)
    g = [set() for _ in range(v_sz)]

    for i, j in product(range(n), range(m)):
        c = gs[i][j]
        i, j = parent[i], parent[n + j]

        if c == "<":
            g[i].add(j)
        elif c == ">":
            g[j].add(i)

    debug_print(vertices, g)

    NIL, VISITED, FINAL = 0, 1, 2
    state = [NIL] * v_sz
    toposort_stack = []

    def visit(v):
        debug_print(v)
        if state[v] == VISITED:
            raise CYCLE

        state[v] = VISITED
        for u in g[v]:
            if state[u] != FINAL:
                visit(u)
        state[v] = FINAL
        toposort_stack.append(v)

    try:
        for v in vertices:
            if state[v] == FINAL:
                continue
            visit(v)
    except CYCLE:
        print('No')
        return

    debug_print(toposort_stack)

    layer = [1] * v_sz

    while toposort_stack:
        v = toposort_stack.pop()
        for u in g[v]:
            layer[u] = max(layer[u], layer[v]+1)

    print('Yes')

    out = []
    for i in range(n):
        out.append(str(layer[parent[i]]))
    print(' '.join(out))

    out = []
    for j in range(m):
        out.append(str(layer[parent[n + j]]))
    print(' '.join(out))

def solve_from_stdin():
    n, m = map(int, input().split())
    gs = []
    for _ in range(n):
        gs.append(input())
    solve(n, m, gs)

solve_from_stdin()


import os
from io import BytesIO
input = BytesIO(os.read(0, os.fstat(0).st_size)).readline

from itertools import product

class CYCLE(Exception):
    ...

debug_print = lambda *args: None

def solve(n, m, gs):
    def union(u, v):
        p, q = find(u), find(v)
        if p == q: return
        if rank[p] < rank[q]: p, q = q, p
        if rank[p] == rank[q]: rank[p] += 1
        parent[q] = p

    def find(u):
        if parent[u] == u: return u
        else:
            p = find(parent[u])
            parent[u] = p
            return p

    rank = [0] * (n + m)
    parent = list(range(n + m))

    for i, j in product(range(n), range(m)):
        if gs[i][j] == "=":
            union(i, n + j)

    vertices = set(parent)
    v_sz = len(vertices)
    g = [set() for _ in range(v_sz)]

    for i, j in product(range(n), range(m)):
        c = gs[i][j]
        i, j = parent[i], parent[n + j]

        if c == "<":
            g[i].add(j)
        elif c == ">":
            g[j].add(i)

    debug_print(vertices, g)

    NIL, VISITED, FINAL = 0, 1, 2
    state = [NIL] * v_sz
    toposort_stack = []

    def visit(v):
        debug_print(v)
        if state[v] == VISITED:
            raise CYCLE

        state[v] = VISITED
        for u in g[v]:
            if state[u] != FINAL:
                visit(u)
        state[v] = FINAL
        toposort_stack.append(v)

    try:
        for v in vertices:
            if state[v] == FINAL:
                continue
            visit(v)
    except CYCLE:
        print('No')
        return

    debug_print(toposort_stack)

    layer = [1] * v_sz

    while toposort_stack:
        v = toposort_stack.pop()
        for u in g[v]:
            layer[u] = max(layer[u], layer[v]+1)

    print('Yes')

    out = []
    for i in range(n):
        out.append(str(layer[parent[i]]))
    print(' '.join(out))

    out = []
    for j in range(m):
        out.append(str(layer[parent[n + j]]))
    print(' '.join(out))

def solve_from_stdin():
    n, m = map(int, input().split())
    gs = []
    for _ in range(n):
        gs.append(input())
    solve(n, m, gs)

solve_from_stdin()


import os
from io import BytesIO
#input = BytesIO(os.read(0, os.fstat(0).st_size)).readline

from itertools import product

class CYCLE(Exception):
    ...

debug_print = lambda *args: None

def solve(n, m, gs):
    def union(u, v):
        p, q = find(u), find(v)
        if p == q: return
        if rank[p] < rank[q]: p, q = q, p
        if rank[p] == rank[q]: rank[p] += 1
        parent[q] = p

    def find(u):
        if parent[u] == u: return u
        else:
            p = find(parent[u])
            parent[u] = p
            return p

    rank = [0] * (n + m)
    parent = list(range(n + m))

    for i, j in product(range(n), range(m)):
        if gs[i][j] == "=":
            union(i, n + j)

    vertices = set(parent)
    v_sz = len(vertices)
    g = [set() for _ in range(n + m)]

    for i, j in product(range(n), range(m)):
        c = gs[i][j]
        i, j = parent[i], parent[n + j]

        if c == "<":
            g[i].add(j)
        elif c == ">":
            g[j].add(i)

    debug_print(vertices, g)

    NIL, VISITED, FINAL = 0, 1, 2
    state = [NIL] * (n + m)
    toposort_stack = []

    def visit(v):
        debug_print(v)
        if state[v] == VISITED:
            raise CYCLE

        state[v] = VISITED
        for u in g[v]:
            if state[u] != FINAL:
                visit(u)
        state[v] = FINAL
        toposort_stack.append(v)

    try:
        for v in vertices:
            if state[v] == FINAL:
                continue
            visit(v)
    except CYCLE:
        print('No')
        return

    debug_print(toposort_stack)

    layer = [1] * (n + m)

    while toposort_stack:
        v = toposort_stack.pop()
        for u in g[v]:
            layer[u] = max(layer[u], layer[v]+1)

    print('Yes')

    out = []
    for i in range(n):
        out.append(str(layer[parent[i]]))
    print(' '.join(out))

    out = []
    for j in range(m):
        out.append(str(layer[parent[n + j]]))
    print(' '.join(out))

def solve_from_stdin():
    n, m = map(int, input().split())
    gs = []
    for _ in range(n):
        gs.append(input())
    solve(n, m, gs)

solve_from_stdin()


@file:Suppress("NOTHING_TO_INLINE", "EXPERIMENTAL_FEATURE_WARNING", "OVERRIDE_BY_INLINE")

import java.io.PrintWriter
import java.util.StringTokenizer
import kotlin.math.*
import kotlin.random.*
import kotlin.system.exitProcess
import kotlin.collections.sort as _sort
import kotlin.collections.sortDescending as _sortDescending
import kotlin.io.println as iprintln

/** @author Spheniscine */
fun main() {
    output {
        val n = readInt()
        val m = readInt()

        val A = readStrings(n)

        val D = DSU(n + m)

        for(i in 0 until n) {
            for(j in 0 until m) {
                if(A[i][j] == '=') D.join(i, n + j)
            }
        }

        val G = Array(n + m) { IntList() }
        val dom = (0 until n + m).toHashSet()

        for(i in 0 until n) {
            for(j in 0 until m) {
                when(A[i][j]) {
                    '>' -> {
                        G[D.root(i)].add(D.root(n + j))
                        dom.remove(n + j)
                    }
                    '<' -> {
                        G[D.root(n + j)].add(D.root(i))
                        dom.remove(i)
                    }
                }
            }
        }

        val ans = IntArray(n + m)
        val seen = BooleanArray(n + m)
        fun dfs(v: Int): Int = run {
            if(seen[v]) fail()
            seen[v] = true
            when {
                ans[v] != 0 -> ans[v]
                G[v].isEmpty() -> 1
                else -> {
                    var max = 0
                    for (c in G[v]) max = max(max, dfs(c))
                    max + 1
                }
            }
        }.also {
            ans[v] = it
            seen[v] = false
        }

        dom.removeIf { D.root(it) != it }
        if(dom.isEmpty()) fail()
        else for (v in dom) dfs(v)

        for(i in 0 until n + m) ans[i] = ans[D.root(i)]

        println("Yes")
        println((0 until n).joinToString(" ") { ans[it].toString() })
        println((0 until m).joinToString(" ") { ans[n + it].toString() })
    }
}

fun fail(): Nothing {
    iprintln("No")
    exitProcess(0)
}

class DSU(n: Int) {
    private val id = IntArray(n) { it }
    private val sz = IntArray(n) { 1 }

    tailrec fun root(v: Int): Int =
        if(id[v] == v) v else {
            id[v] = id[id[v]]
            root(id[v])
        }

    /** @return false if the two vertices are already joined, true if the join operation is successful */
    fun join(u: Int, v: Int): Boolean {
        var ru = root(u)
        var rv = root(v)
        if(ru == rv) return false
        if(sz[ru] < sz[rv]) ru = rv.also { rv = ru }
        id[rv] = id[ru]
        sz[ru] += sz[rv]
        return true
    }

    fun isJoined(u: Int, v: Int) = root(u) == root(v)

    fun size(v: Int) = sz[root(v)]
}

class IntList(initialCapacity: Int = 12) {
    var _arr = IntArray(initialCapacity)
    private val capacity get() = _arr.size
    var size = 0
        private set
    inline val lastIndex get() = size - 1
    inline val indices get() = 0 until size

    private fun grow(minCapacity: Int = 2) {
        val newCapacity = maxOf(minCapacity, capacity + (capacity shr 1))
        _arr = _arr.copyOf(newCapacity)
    }

    operator fun get(index: Int): Int {
        require(index in 0 until size)
        return _arr[index]
    }

    operator fun set(index: Int, value: Int) {
        require(index in 0 until size)
        _arr[index] = value
    }

    fun add(value: Int) {
        if(size == capacity) grow()
        _arr[size++] = value
    }

    fun removeLast() = _arr[--size]

    fun clear() { size = 0 }

    operator fun iterator() = object: IntIterator() {
        private var pos = 0
        override fun hasNext() = pos < size
        override fun nextInt() = get(pos++)
    }

    fun isEmpty() = size == 0
}

/** IO code start */
//@JvmField val INPUT = File("input.txt").inputStream()
//@JvmField val OUTPUT = File("output.txt").outputStream()
@JvmField val INPUT = System.`in`
@JvmField val OUTPUT = System.out

@JvmField val _reader = INPUT.bufferedReader()
fun readLine(): String? = _reader.readLine()
fun readLn() = _reader.readLine()!!
@JvmField var _tokenizer: StringTokenizer = StringTokenizer("")
fun read(): String {
    while (_tokenizer.hasMoreTokens().not()) _tokenizer = StringTokenizer(_reader.readLine() ?: return "", " ")
    return _tokenizer.nextToken()
}
fun readInt() = read().toInt()
fun readDouble() = read().toDouble()
fun readLong() = read().toLong()
fun readStrings(n: Int) = List(n) { read() }
fun readLines(n: Int) = List(n) { readLn() }
fun readInts(n: Int) = List(n) { read().toInt() }
fun readIntArray(n: Int) = IntArray(n) { read().toInt() }
fun readDoubles(n: Int) = List(n) { read().toDouble() }
fun readDoubleArray(n: Int) = DoubleArray(n) { read().toDouble() }
fun readLongs(n: Int) = List(n) { read().toLong() }
fun readLongArray(n: Int) = LongArray(n) { read().toLong() }

@JvmField val _writer = PrintWriter(OUTPUT, false)
inline fun output(block: PrintWriter.() -> Unit) { _writer.apply(block).flush() }

/** shuffles and sort overrides to avoid quicksort attacks */
private inline fun <R, V> R._shuffle(rnd: Random, get: R.(Int) -> V, set: R.(Int, V) -> Unit, size: Int) {
    // Fisher-Yates shuffle algorithm
    for (i in size - 1 downTo 1) {
        val j = rnd.nextInt(i + 1)
        val temp = get(i)
        set(i, get(j))
        set(j, temp)
    }
}

@JvmField var _random: Random? = null
val random get() = _random ?: Random(0x594E215C123 * System.nanoTime()).also { _random = it }

fun IntArray.shuffle(rnd: Random = random) = _shuffle(rnd, IntArray::get, IntArray::set, size)
fun IntArray.sort() { shuffle(); _sort() }
fun IntArray.sortDescending() { shuffle(); _sortDescending() }

fun LongArray.shuffle(rnd: Random = random) = _shuffle(rnd, LongArray::get, LongArray::set, size)
fun LongArray.sort() { shuffle(); _sort() }
fun LongArray.sortDescending() { shuffle(); _sortDescending() }

fun DoubleArray.shuffle(rnd: Random = random) = _shuffle(rnd, DoubleArray::get, DoubleArray::set, size)
fun DoubleArray.sort() { shuffle(); _sort() }
fun DoubleArray.sortDescending() { shuffle(); _sortDescending() }

fun CharArray.shuffle(rnd: Random = random) = _shuffle(rnd, CharArray::get, CharArray::set, size)
inline fun CharArray.sort() { _sort() }
inline fun CharArray.sortDescending() { _sortDescending() }

inline fun <T: Comparable<T>> Array<out T>.sort() = _sort()
inline fun <T: Comparable<T>> Array<out T>.sortDescending() = _sortDescending()
inline fun <T: Comparable<T>> MutableList<out T>.sort() = _sort()
inline fun <T: Comparable<T>> MutableList<out T>.sortDescending() = _sortDescending()

fun `please stop removing these imports IntelliJ`() {
    iprintln(max(1, 2))
}