#include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 1000000000000000000
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


#define SIZE 2000

struct Node{
	void set(bool arg_is_leaf,int arg_comp_id){
		is_leaf = arg_is_leaf;
		comp_id = arg_comp_id;
	}
	bool is_leaf;
	int comp_id;
};

int N;
int table[55][55];
int dist_table[SIZE][55]; //dist_table[swicthのnodes上のindex][compのid] = compからの距離
int num_nodes;
int num_leaf,num_switch;
int loc[55];
int num_adj[SIZE],adj_list[SIZE][SIZE];
int leaf_index[55],switch_index[SIZE];
bool FLG;
vector<int> ans;
Node nodes[SIZE];



void calc_dist(int base_num,int node_id,int pre,int dist){

	if(!nodes[node_id].is_leaf){

		dist_table[node_id][base_num] = dist; //★★距離テーブルはコンピュータのidそのもの★★
	}

	for(int i = 0; i < num_adj[node_id]; i++){

		int next = adj_list[node_id][i];
		if(next == pre)continue;

		calc_dist(base_num,next,node_id,dist+1);
	}
}

void recursive(int num_rest){
	if(FLG)return;

	if(num_rest == 0){
		FLG = true;
		for(int i = 0; i < num_nodes; i++){
			if(!nodes[i].is_leaf){
				ans.push_back(num_adj[i]);
			}
		}
		return;
	}

	int new_node = -1;

	//新しいノードを1つ選ぶ
	for(int i = 0; i < N; i++){
		if(loc[i] == -1){

			new_node = i;
			break;
		}
	}

	//辻褄の合うswitchを全部試す
	for(int i = 0; i < num_switch; i++){

		int tmp_switch = switch_index[i];
		bool FLG = true;
		int tmp = -1;

		for(int k = 0; k < num_leaf; k++){

			int comp_id = nodes[leaf_index[k]].comp_id;
			int diff = table[comp_id][new_node]-dist_table[tmp_switch][comp_id];

			if(diff <= 0){

				FLG = false;
				break;
			}

			if(tmp == -1){

				tmp = diff;
			}else{
				if(tmp != diff){ //追加するべきパスの長さが一意にならないと不可

					FLG = false;
					break;
				}
			}
		}

		//continue;

		if(FLG){ //tmp_swichから,tmp-1個のswitchを伸ばし、その先にnew_nodeを張る

			int pre_num_nodes = num_nodes;
			int pre_num_switch = num_switch;

			if(tmp-1 > 0){

				int add = 1;

				adj_list[tmp_switch][num_adj[tmp_switch]++] = num_nodes;
				adj_list[num_nodes][num_adj[num_nodes]++] = tmp_switch;

				nodes[num_nodes].set(false,-1);
				//距離表を更新
				for(int a = 0; a < N; a++){
					if(dist_table[tmp_switch][a] == BIG_NUM)continue;
					dist_table[num_nodes][a] = dist_table[tmp_switch][a]+add;
				}
				add++;

				switch_index[num_switch++] = num_nodes;
				num_nodes++;

				for(int k = 0; k < tmp-2; k++){

					adj_list[num_nodes-1][num_adj[num_nodes-1]++] = num_nodes;
					adj_list[num_nodes][num_adj[num_nodes]++] = num_nodes-1;

					nodes[num_nodes].set(false,-1);
					//距離表を更新
					for(int a = 0; a < N; a++){
						if(dist_table[tmp_switch][a] == BIG_NUM)continue;
						dist_table[num_nodes][a] = dist_table[tmp_switch][a]+add;
					}
					add++;

					switch_index[num_switch++] = num_nodes;
					num_nodes++;
				}

				//new_nodeへの辺を張る
				adj_list[num_nodes-1][num_adj[num_nodes-1]++] = num_nodes;
				adj_list[num_nodes][num_adj[num_nodes]++] = num_nodes-1;

				nodes[num_nodes].set(true,new_node);
				leaf_index[num_leaf] = num_nodes;
				loc[new_node] = num_nodes;

				num_leaf++;
				num_nodes++;

			}else{ //tmp-1 == 0

				//new_nodeへの辺を張る
				adj_list[tmp_switch][num_adj[tmp_switch]++] = num_nodes;
				adj_list[num_nodes][num_adj[num_nodes]++] = tmp_switch;

				nodes[num_nodes].set(true,new_node);
				leaf_index[num_leaf] = num_nodes;
				loc[new_node] = num_nodes;

				num_leaf++;
				num_nodes++;
			}

			calc_dist(new_node,loc[new_node],-1,0);
			recursive(num_rest-1);

			for(int i = pre_num_nodes; i < num_nodes; i++){

				num_adj[i] = 0;
			}
			num_adj[tmp_switch]--;

			num_switch = pre_num_switch;
			num_nodes = pre_num_nodes;

			num_leaf--;
			loc[new_node] = -1;
		}
	}
}


void func(){

	FLG = false;
	num_nodes = 0;
	num_leaf = 0;
	num_switch = 0;
	ans.clear();

	for(int i = 0; i < SIZE; i++){

		num_adj[i] = 0;
	}
	for(int i = 0; i < N; i++){

		loc[i] = -1; //逆引き表
	}
	for(int i = 0; i < SIZE; i++){
		for(int k = 0; k < N; k++){

			dist_table[i][k] = BIG_NUM;
		}
	}


	int a,b,max_dist = 0;

	for(int row = 0; row < N; row++){
		for(int col = 0; col < N; col++){

			scanf("%d",&table[row][col]);
			if(max_dist < table[row][col]){
				max_dist = table[row][col];
				a = min(row,col);
				b = max(row,col);
			}
		}
	}

	//a-b間にmax_dist-1個のswitchを配置
	nodes[0].set(true,a);
	leaf_index[0] = 0; //★★nodes上のindex★★
	loc[a] = 0;
	num_nodes = 1;

	for(int i = 1; i <= max_dist-1; i++){

		adj_list[i-1][num_adj[i-1]++] = i; //左隣の隣接に自分を追加
		adj_list[i][num_adj[i]++] = i-1; //自分の隣接に左隣を追加


		nodes[i].set(false,-1);
		switch_index[num_switch++] = i;
		num_nodes++;
	}
	nodes[num_nodes].set(true,b);
	loc[b] = num_nodes;
	leaf_index[1] = num_nodes;

	adj_list[num_nodes-1][num_adj[num_nodes-1]++] = num_nodes;
	adj_list[num_nodes][num_adj[num_nodes]++] = num_nodes-1;

	num_nodes++;

	num_leaf = 2;

	calc_dist(a,loc[a],-1,0);
	calc_dist(b,loc[b],-1,0);

	recursive(N-2);

	sort(ans.begin(),ans.end());
	printf("%d",ans[0]);
	for(int i = 1; i < ans.size(); i++){

		printf(" %d",ans[i]);
	}
	printf("\n");
}

int main(){

	while(true){
		scanf("%d",&N);
		if(N == 0)break;

		func();
	}

	return 0;
}



Measured memory_bytes: 6732