Problem:
Avoiding Three Cs
You are a member of ICPC Company, and planning to place seats in the new office.
    Your task is to place as many seats as possible in the room while
    satisfying the conditions for
avoiding three Cs
.
    You decided to solve the problem through simulation on a grid.
In the simulation, the room is expressed by a rectangular grid.
    Each cell in the grid is either empty where you can place a seat,
    or a wall cell where you cannot place a seat.
    You cannot place two or more seats in the same empty cell
    to keep the distance between seats.
Hereinafter, a route starting from the cell at the northwest corner,
    repeating moves to the east or to the south, finally reaching the cell
    at the southeast corner, is called a
ventilation route.
There may exist many ventilation routes.
    For any cell with a seat placed, it must be on at least one ventilation route.
    Moreover, the number of seats on any ventilation route,
    called
the seat passing number,
must not exceed the given
    maximum.

    Your task is to find seat placements in the room that have as many seats as possible.
Input
The input consists of multiple datasets.
    Each dataset is represented in the following format.
n
m
k
a
1,1
...
a
1,
m
...
a
n,
1
...
a
n,m
n
and
m
are the height and width of the given grid.
n
and
m
are positive integers not exceeding 20.
k
is the maximum allowed seat passing number.
k
is a positive integer not exceeding
n + m
− 1.
    Each of the following
n
lines contains
m
characters,
    either '
.
' or '
#
', denoting the cells of the grid.
    If
a
i,j
equals '
.
', the corresponding cell is empty.
    If it equals '
#
', the cell is a wall.
    The cell at row 1 and column 1 is at the northwest corner of the room,
    and the cell at row
n
and column
m
is at the southeast corner.
The end of the input is indicated by a line containing three zeros.
    The number of datasets does not exceed 100.
Output
For each of the datasets, output the following
n
+ 1 lines.
    The first line should contain the maximum possible number of seats that can be placed.
    The second and subsequent lines should contain one of the placements
    with the maximum number of seats.
    The output of the placements should be in the same format as given in the input,
    except that cells with a seat placed should be
    represented as  '
@
' instead of  '
.
'.
    If there are two or more placements with the maximum possible number of seats,
    output one with the first in the dictionary order in the ASCII codes
    when all the lines of their representations are concatenated.
    Note that '
#
' < '
.
' < '
@
' holds in ASCII codes.
Sample Input
3 3 3
...
.#.
...
3 3 1
...
.#.
...
6 11 1
...........
.....#.....
.....#.....
.....#.....
.....#.....
...........
7 7 3
.......
.##.##.
.#...#.
.#...#.
.#...#.
.##.##.
.......
0 0 0
Output for the Sample Input
6
.@@
@#@
@@.
2
...
.#@
.@.
10
..........@
....@#...@.
...@.#..@..
..@..#.@...
.@...#@....
@...
...(truncated)

Submitted solutions:

code_snippet: #include<bits/stdc++.h>
using namespace std;

#include <algorithm>
#include <cassert>
#include <limits>
#include <queue>
#include <vector>

namespace atcoder {

template <class Cap, class Cost> struct mcf_graph {
  public:
    mcf_graph() {}
    mcf_graph(int n) : _n(n), g(n) {}

    int add_edge(int from, int to, Cap cap, Cost cost) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        int m = int(pos.size());
        pos.push_back({from, int(g[from].size())});
        int from_id = int(g[from].size());
        int to_id = int(g[to].size());
        if (from == to) to_id++;
        g[from].push_back(_edge{to, to_id, cap, cost});
        g[to].push_back(_edge{from, from_id, 0, -cost});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
        Cost cost;
    };

    edge get_edge(int i) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        auto _e = g[pos[i].first][pos[i].second];
        auto _re = g[_e.to][_e.rev];
        return edge{
            pos[i].first, _e.to, _e.cap + _re.cap, _re.cap, _e.cost,
        };
    }
    std::vector<edge> edges() {
        int m = int(pos.size());
        std::vector<edge> result(m);
        for (int i = 0; i < m; i++) {
            result[i] = get_edge(i);
        }
        return result;
    }

    std::pair<Cap, Cost> flow(int s, int t) {
        return flow(s, t, std::numeric_limits<Cap>::max());
    }
    std::pair<Cap, Cost> flow(int s, int t, Cap flow_limit) {
        return slope(s, t, flow_limit).back();
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t) {
        return slope(s, t, std::numeric_limits<Cap>::max());
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);
        // variants (C = maxcost):
        // -(n-1)C <= dual[s] <= dual[i] <= dual[t] = 0
        // reduced cost (= e.cost + dual[e.from] - dual[e.to]) >= 0 for all edge
        std::vector<Cost> dual(_n, 0), dist(_n);
        std::vector<int> pv(_n), pe(_n);
        std::vector<bool> vis(_n);
        auto dual_ref = [&]() {
            std::fill(dist.begin(), dist.end(),
                      std::numeric_limits<Cost>::max());
            std::fill(pv.begin(), pv.end(), -1);
            std::fill(pe.begin(), pe.end(), -1);
            std::fill(vis.begin(), vis.end(), false);
            struct Q {
                Cost key;
                int to;
                bool operator<(Q r) const { return key > r.key; }
            };
            std::priority_queue<Q> que;
            dist[s] = 0;
            que.push(Q{0, s});
            while (!que.empty()) {
                int v = que.top().to;
                que.pop();
                if (vis[v]) continue;
                vis[v] = true;
                if (v == t) break;
                // dist[v] = shortest(s, v) + dual[s] - dual[v]
                // dist[v] >= 0 (all reduced cost are positive)
                // dist[v] <= (n-1)C
                for (int i = 0; i < int(g[v].size()); i++) {
                    auto e = g[v][i];
                    if (vis[e.to] || !e.cap) continue;
                    // |-dual[e.to] + dual[v]| <= (n-1)C
                    // cost <= C - -(n-1)C + 0 = nC
                    Cost cost = e.cost - dual[e.to] + dual[v];
                    if (dist[e.to] - dist[v] > cost) {
                        dist[e.to] = dist[v] + cost;
                        pv[e.to] = v;
                        pe[e.to] = i;
                        que.push(Q{dist[e.to], e.to});
                    }
                }
            }
            if (!vis[t]) {
                return false;
            }

            for (int v = 0; v < _n; v++) {
                if (!vis[v]) continue;
                // dual[v] = dual[v] - dist[t] + dist[v]
                //         = dual[v] - (shortest(s, t) + dual[s] - dual[t]) + (shortest(s, v) + dual[s] - dual[v])
                //         = - shortest(s, t) + dual[t] + shortest(s, v)
                //         = shortest(s, v) - shortest(s, t) >= 0 - (n-1)C
                dual[v] -= dist[t] - dist[v];
            }
            return true;
        };
        Cap flow = 0;
        Cost cost = 0, prev_cost_per_flow = -1;
        std::vector<std::pair<Cap, Cost>> result;
        result.push_back({flow, cost});
        while (flow < flow_limit) {
            if (!dual_ref()) break;
            Cap c = flow_limit - flow;
            for (int v = t; v != s; v = pv[v]) {
                c = std::min(c, g[pv[v]][pe[v]].cap);
            }
            for (int v = t; v != s; v = pv[v]) {
                auto& e = g[pv[v]][pe[v]];
                e.cap -= c;
                g[v][e.rev].cap += c;
            }
            Cost d = -dual[s];
            flow += c;
            cost += c * d;
            if (prev_cost_per_flow == d) {
                result.pop_back();
            }
            result.push_back({flow, cost});
            prev_cost_per_flow = d;
        }
        return result;
    }

  private:
    int _n;

    struct _edge {
        int to, rev;
        Cap cap;
        Cost cost;
    };

    std::vector<std::pair<int, int>> pos;
    std::vector<std::vector<_edge>> g;
};

}  // namespace atcoder

#define rep(i, s, n) for (int i = (int)(s); i < (int)(n); i++)
#define rrep(i, s, n) for (int i = (int)(n)-1; i >= (int)(s); i--)


// なにこれ
// 全然正当性がよく分からない

void solve(int n, int m,int k){
	vector<string> a(n);
	rep(i,0,n) cin >> a[i];
	
	rep(i,0,n){
		rep(j,0,m){
			if (a[i][j] == '.') a[i][j] = '?';
		}
	}

	auto check = [&]() -> int {
		int siz = n*m;
		atcoder::mcf_graph<int,int> mcf(2*siz+2);
		mcf.add_edge(2*siz, 0, siz, 0);
		mcf.add_edge(2*siz-1, 2*siz+1, siz, 0);
		
		rep(i,0,n){
			rep(j,0,m){
				if (a[i][j] == '?' || a[i][j] == '@'){
					mcf.add_edge(i*m+j, i*m+j+siz, 1, 0);
				}
				mcf.add_edge(i*m+j, i*m+j+siz, siz, 1);
			}
		}

		rep(i,0,n-1){
			rep(j,0,m){
				if (a[i][j] == '#' || a[i+1][j] == '#') continue;
				mcf.add_edge(i*m+j+siz, (i+1)*m+j, siz, 0);
			}
		}
		
		rep(i,0,n){
			rep(j,0,m-1){
				if (a[i][j] == '#' || a[i][j+1] == '#') continue;
				mcf.add_edge(i*m+j+siz, i*m+j+1, siz, 0);
			}
		}

		vector<pair<int,int>> sl = mcf.slope(siz*2, siz*2+1);
		int ret = 0;
		rep(i,0,(int)sl.size()-1){
			int now_cost = sl[i].second;
			int nxt_cost = sl[i+1].second;
			int now_cap = sl[i].first;
			int nxt_cap = sl[i+1].first;

			int increase = n+m-1 - (nxt_cost - now_cost) / (nxt_cap - now_cap);

			rep(x,0,nxt_cap - now_cap){
				ret += min(k, increase);
			}
		}
		return ret;
	};

	vector<vector<int>> dp(n, vector<int>(m));
	int max_seat = check();

	rep(i,0,n){
		rep(j,0,m){
			if (a[i][j] == '#') continue;
			
			if (i-1 >= 0) dp[i][j] = max(dp[i][j], dp[i-1][j]);
			if (j-1 >= 0) dp[i][j] = max(dp[i][j], dp[i][j-1]);
			
			if (dp[i][j] == k){
				a[i][j] = '.';
				continue;
			}
			
			a[i][j] = '.';
			if (check() != max_seat){
				a[i][j] = '@';
				dp[i][j] += 1;
			}
		}
	}

	cout << max_seat << '\n';
	rep(i,0,n){
		cout << a[i] << '\n';
	}
}

int main(){
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	
	while(true){
		int n, m, k; cin >> n >> m >> k;
		if (n == 0) return 0;
		solve(n, m, k);
	}
}
accuracy: 1
time_ms: 6430
memory_kb: 3572
final_rank: 2

code_snippet: #include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 4000000000000000000 //オーバーフローに注意
#define MOD 1000000007
#define EPS 0.000000001





#define MAX 60
#define NUM 805
#define SIZE 25



//辺を表す構造体{行先、容量、コスト、逆辺のインデックス}
struct Edge{
	Edge(int arg_to,int arg_capacity,int arg_cost,int arg_rev_index){
		to = arg_to;
		capacity = arg_capacity;
		cost = arg_cost;
		rev_index = arg_rev_index;
	}

	int to,cost,capacity,rev_index;
};


int H,W,K;
char input[SIZE][SIZE],ANS[SIZE][SIZE];


int V; //頂点数
vector<Edge> G[NUM]; //グラフの隣接リスト表現
int h[NUM]; //ポテンシャル
int dist[NUM]; //最短距離
int pre_node[NUM],pre_edge[NUM]; //直前の頂点と辺
int IN[405],OUT[405],dp[SIZE][SIZE];


void outPut(){

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

			printf("%c",ANS[row][col]);
		}
		printf("\n");
	}
}

int getIndex(int row,int col){

	return row*W + col;
}

bool is_R(int row,int col){

	return col+1 < W && input[row][col+1] != '#';
}

bool is_L(int row,int col){

	return col-1 >= 0 && input[row][col-1] != '#';
}


bool is_U(int row,int col){

	return row-1 >= 0 && input[row-1][col] != '#';
}

bool is_D(int row,int col){

	return row+1 <= H-1 && input[row+1][col] != '#';
}




typedef pair<int,int> P; //firstは最短距離、secondは頂点の番号



//fromからtoへ向かう容量capacity,コストcostの辺をグラフに追加する
void add_edge(int from,int to,int capacity,int cost){
	G[from].push_back(Edge(to,capacity,cost,G[to].size()));
	G[to].push_back(Edge(from,0,-cost,G[from].size()-1));
}

int min_cost_flow(int source,int sink,int flow){

	int ret = 0;
	for(int i = 0; i < V; i++)h[i] = 0; //ポテンシャルを0にする

	while(flow > 0){
		//ダイクストラ法を用いてhを更新する
		priority_queue<P,vector<P>,greater<P>> Q;

		for(int i = 0; i < V; i++)dist[i] = BIG_NUM;

		dist[source] = 0;
		Q.push(P(0,source));

		while(!Q.empty()){
			P p = Q.top();
			Q.pop();
			int node_id = p.second;
			if(dist[node_id] < p.first)continue; //最短でなければSKIP
			for(int i = 0; i < G[node_id].size(); i++){
				Edge &e = G[node_id][i];
				if(e.capacity > 0 && dist[e.to] > dist[node_id]+e.cost+h[node_id]-h[e.to]){
					dist[e.to] = dist[node_id]+e.cost+h[node_id]-h[e.to];
					pre_node[e.to] = node_id;
					pre_edge[e.to] = i;
					Q.push(P(dist[e.to],e.to));
				}
			}
		}

		if(dist[sink] == BIG_NUM){
			//これ以上流せない
			return ret;
		}

		for(int node_id = 0; node_id < V; node_id++)h[node_id] += dist[node_id];

		//source-sink間最短路に沿って目いっぱい流す
		int tmp_flow = flow;
		for(int node_id = sink; node_id != source; node_id = pre_node[node_id]){
			tmp_flow = min(tmp_flow,G[pre_node[node_id]][pre_edge[node_id]].capacity);
		}
		flow -= tmp_flow;

		int mult = min(K,(H+W-1)-h[sink]); //■Kの制約

		ret += tmp_flow*mult;

		for(int node_id = sink; node_id != source; node_id = pre_node[node_id]){
			Edge &e = G[pre_node[node_id]][pre_edge[node_id]];
			e.capacity -= tmp_flow;
			G[node_id][e.rev_index].capacity += tmp_flow;
		}
	}
	return ret;
}



int calc_maxi(int source,int sink,int not_row,int not_col){


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

		G[i].clear();
	}

	int CAP = H*W;

	add_edge(source,IN[getIndex(0,0)],CAP,0);
	add_edge(OUT[getIndex(H-1,W-1)],sink,CAP,0);

	for(ll row = 0; row < H; row++){
		for(ll col = 0; col < W; col++){
			if(input[row][col] == '#')continue;

			int id = getIndex(row,col);

			if((row == not_row && col == not_col) || (ANS[row][col] == '-')){ //■置かない

				add_edge(IN[id],OUT[id],CAP,1);

			}else{ //■置く

				add_edge(IN[id],OUT[id],1,0);
				add_edge(IN[id],OUT[id],CAP-1,1);
			}

			if(is_R(row,col)){
				int adj = getIndex(row,col+1);
				add_edge(OUT[id],IN[adj],CAP,0);
			}
			if(is_D(row,col)){
				int adj = getIndex(row+1,col);
				add_edge(OUT[id],IN[adj],CAP,0);
			}
		}
	}

	return min_cost_flow(source,sink,CAP);
}


void func(){

	for(int row = 0; row < H; row++){

		scanf("%s",input[row]);
		for(int col = 0; col < W; col++){

			ANS[row][col] = input[row][col];
		}
	}

	if(input[0][0] == '#' || input[H-1][W-1] == '#'){

		printf("0\n");
		outPut();
		return;
	}

	int index = 0;
	for(int row = 0; row < H; row++){
		for(int col = 0; col < W; col++){
			if(input[row][col] == '#')continue;

			ll id = getIndex(row,col);
			IN[id] = index++;
			OUT[id] = index++;
		}
	}

	int source = index,sink = index+1;
	V = index+2;

	//最大の設置個数を求める
	int maxi = calc_maxi(source,sink,-1,-1);
	printf("%d\n",maxi);

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

			dp[row][col] = 0;
		}
	}

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

			if(input[row][col] == '#')continue;

			if(row-1 >= 0){

				dp[row][col] = max(dp[row][col],dp[row-1][col]);
			}
			if(col-1 >= 0){

				dp[row][col] = max(dp[row][col],dp[row][col-1]);
			}

			if(dp[row][col] == K){ //もう置けない

				ANS[row][col] = '-';
				continue;
			}

			ll tmp = calc_maxi(source,sink,row,col);

			if(tmp != maxi){ //■置かないと最大値を実現できない

				ANS[row][col] = '@';
				dp[row][col]++;

			}else{ //■置かなくても最大値を実現できる

				ANS[row][col] = '-'; //■"@"をなるべく後ろに置く
			}
		}
	}

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

			if(ANS[row][col] == '-'){

				ANS[row][col] = '.';
			}
		}
	}

	outPut();
	fflush(stdout);
}

int main(){

	while(true){

		scanf("%d %d %d",&H,&W,&K);
		if(H == 0 && W == 0 && K == 0)break;

		func();
	}

	return 0;
}
accuracy: 1
time_ms: 7630
memory_kb: 3672
final_rank: 6

code_snippet: #include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 4000000000000000000 //オーバーフローに注意
#define MOD 1000000007
#define EPS 0.000000001





#define MAX 60
#define NUM 805
#define SIZE 25



//辺を表す構造体{行先、容量、コスト、逆辺のインデックス}
struct Edge{
	Edge(int arg_to,int arg_capacity,int arg_cost,int arg_rev_index){
		to = arg_to;
		capacity = arg_capacity;
		cost = arg_cost;
		rev_index = arg_rev_index;
	}

	int to,cost,capacity,rev_index;
};


int H,W,K;
char input[SIZE][SIZE],ANS[SIZE][SIZE];


int V; //頂点数
vector<Edge> G[NUM]; //グラフの隣接リスト表現
int h[NUM]; //ポテンシャル
int dist[NUM]; //最短距離
int pre_node[NUM],pre_edge[NUM]; //直前の頂点と辺
int IN[405],OUT[405],dp[SIZE][SIZE];


void outPut(){

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

			printf("%c",ANS[row][col]);
		}
		printf("\n");
	}
}

int getIndex(int row,int col){

	return row*W + col;
}

bool is_R(int row,int col){

	return col+1 < W && input[row][col+1] != '#';
}

bool is_L(int row,int col){

	return col-1 >= 0 && input[row][col-1] != '#';
}


bool is_U(int row,int col){

	return row-1 >= 0 && input[row-1][col] != '#';
}

bool is_D(int row,int col){

	return row+1 <= H-1 && input[row+1][col] != '#';
}




typedef pair<int,int> P; //firstは最短距離、secondは頂点の番号



//fromからtoへ向かう容量capacity,コストcostの辺をグラフに追加する
void add_edge(int from,int to,int capacity,int cost){
	G[from].push_back(Edge(to,capacity,cost,G[to].size()));
	G[to].push_back(Edge(from,0,-cost,G[from].size()-1));
}

int min_cost_flow(int source,int sink,int flow){

	int ret = 0;
	for(int i = 0; i < V; i++)h[i] = 0; //ポテンシャルを0にする

	while(flow > 0){
		//ダイクストラ法を用いてhを更新する
		priority_queue<P,vector<P>,greater<P>> Q;

		for(int i = 0; i < V; i++)dist[i] = BIG_NUM;

		dist[source] = 0;
		Q.push(P(0,source));

		while(!Q.empty()){
			P p = Q.top();
			Q.pop();
			int node_id = p.second;
			if(dist[node_id] < p.first)continue; //最短でなければSKIP
			for(int i = 0; i < G[node_id].size(); i++){
				Edge &e = G[node_id][i];
				if(e.capacity > 0 && dist[e.to] > dist[node_id]+e.cost+h[node_id]-h[e.to]){
					dist[e.to] = dist[node_id]+e.cost+h[node_id]-h[e.to];
					pre_node[e.to] = node_id;
					pre_edge[e.to] = i;
					Q.push(P(dist[e.to],e.to));
				}
			}
		}

		if(dist[sink] == BIG_NUM){
			//これ以上流せない
			return ret;
		}

		for(int node_id = 0; node_id < V; node_id++)h[node_id] += dist[node_id];

		//source-sink間最短路に沿って目いっぱい流す
		int tmp_flow = flow;
		for(int node_id = sink; node_id != source; node_id = pre_node[node_id]){
			tmp_flow = min(tmp_flow,G[pre_node[node_id]][pre_edge[node_id]].capacity);
		}
		flow -= tmp_flow;

		int mult = min(K,(H+W-1)-h[sink]); //■Kの制約

		ret += tmp_flow*mult;

		for(int node_id = sink; node_id != source; node_id = pre_node[node_id]){
			Edge &e = G[pre_node[node_id]][pre_edge[node_id]];
			e.capacity -= tmp_flow;
			G[node_id][e.rev_index].capacity += tmp_flow;
		}
	}
	return ret;
}



int calc_maxi(int source,int sink,int not_row,int not_col){


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

		G[i].clear();
	}

	int CAP = H*W; //■1回の流れでは、なるべくコストを小さくするので、集められるだけの@が集まる

	add_edge(source,IN[getIndex(0,0)],CAP,0);
	add_edge(OUT[getIndex(H-1,W-1)],sink,CAP,0);

	for(ll row = 0; row < H; row++){
		for(ll col = 0; col < W; col++){
			if(input[row][col] == '#')continue;

			int id = getIndex(row,col);

			if((row == not_row && col == not_col) || (ANS[row][col] == '-')){ //■置かない

				add_edge(IN[id],OUT[id],CAP,1);

			}else{ //■置く

				add_edge(IN[id],OUT[id],1,0);
				add_edge(IN[id],OUT[id],CAP-1,1);
			}

			if(is_R(row,col)){
				int adj = getIndex(row,col+1);
				add_edge(OUT[id],IN[adj],CAP,0);
			}
			if(is_D(row,col)){
				int adj = getIndex(row+1,col);
				add_edge(OUT[id],IN[adj],CAP,0);
			}
		}
	}

	return min_cost_flow(source,sink,CAP);
}


void func(){

	for(int row = 0; row < H; row++){

		scanf("%s",input[row]);
		for(int col = 0; col < W; col++){

			ANS[row][col] = input[row][col];
		}
	}

	if(input[0][0] == '#' || input[H-1][W-1] == '#'){

		printf("0\n");
		outPut();
		return;
	}

	int index = 0;
	for(int row = 0; row < H; row++){
		for(int col = 0; col < W; col++){
			if(input[row][col] == '#')continue;

			ll id = getIndex(row,col);
			IN[id] = index++;
			OUT[id] = index++;
		}
	}

	int source = index,sink = index+1;
	V = index+2;

	//最大の設置個数を求める
	int maxi = calc_maxi(source,sink,-1,-1);
	printf("%d\n",maxi);

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

			dp[row][col] = 0;
		}
	}

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

			if(input[row][col] == '#')continue;

			if(row-1 >= 0){

				dp[row][col] = max(dp[row][col],dp[row-1][col]);
			}
			if(col-1 >= 0){

				dp[row][col] = max(dp[row][col],dp[row][col-1]);
			}

			if(dp[row][col] == K){ //もう置けない

				ANS[row][col] = '-';
				continue;
			}

			ll tmp = calc_maxi(source,sink,row,col);

			if(tmp != maxi){ //■置かないと最大値を実現できない

				ANS[row][col] = '@';
				dp[row][col]++;

			}else{ //■置かなくても最大値を実現できる

				ANS[row][col] = '-'; //■"@"をなるべく後ろに置く
			}
		}
	}

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

			if(ANS[row][col] == '-'){

				ANS[row][col] = '.';
			}
		}
	}

	outPut();
	fflush(stdout);
}

int main(){

	while(true){

		scanf("%d %d %d",&H,&W,&K);
		if(H == 0 && W == 0 && K == 0)break;

		func();
	}

	return 0;
}
accuracy: 1
time_ms: 7630
memory_kb: 3588
final_rank: 5

code_snippet: #include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 4000000000000000000 //オーバーフローに注意
#define MOD 1000000007
#define EPS 0.000000001





#define MAX 60
#define NUM 805
#define SIZE 25



//辺を表す構造体{行先、容量、コスト、逆辺のインデックス}
struct Edge{
	Edge(int arg_to,int arg_capacity,int arg_cost,int arg_rev_index){
		to = arg_to;
		capacity = arg_capacity;
		cost = arg_cost;
		rev_index = arg_rev_index;
	}

	int to,cost,capacity,rev_index;
};


int H,W,K;
char input[SIZE][SIZE],ANS[SIZE][SIZE];


int V; //頂点数
vector<Edge> G[NUM]; //グラフの隣接リスト表現
int h[NUM]; //ポテンシャル
int dist[NUM]; //最短距離
int pre_node[NUM],pre_edge[NUM]; //直前の頂点と辺
int IN[405],OUT[405],dp[SIZE][SIZE];


void outPut(){

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

			printf("%c",ANS[row][col]);
		}
		printf("\n");
	}
}

int getIndex(int row,int col){

	return row*W + col;
}

bool is_R(int row,int col){

	return col+1 < W && input[row][col+1] != '#';
}

bool is_L(int row,int col){

	return col-1 >= 0 && input[row][col-1] != '#';
}


bool is_U(int row,int col){

	return row-1 >= 0 && input[row-1][col] != '#';
}

bool is_D(int row,int col){

	return row+1 <= H-1 && input[row+1][col] != '#';
}




typedef pair<ll,int> P; //firstは最短距離、secondは頂点の番号



//fromからtoへ向かう容量capacity,コストcostの辺をグラフに追加する
void add_edge(int from,int to,int capacity,int cost){
	G[from].push_back(Edge(to,capacity,cost,G[to].size()));
	G[to].push_back(Edge(from,0,-cost,G[from].size()-1));
}

int min_cost_flow(int source,int sink,int flow){

	int ret = 0;
	for(int i = 0; i < V; i++)h[i] = 0; //ポテンシャルを0にする

	while(flow > 0){
		//ダイクストラ法を用いてhを更新する
		priority_queue<P,vector<P>,greater<P>> Q;

		for(int i = 0; i < V; i++)dist[i] = BIG_NUM;

		dist[source] = 0;
		Q.push(P(0,source));

		while(!Q.empty()){
			P p = Q.top();
			Q.pop();
			int node_id = p.second;
			if(dist[node_id] < p.first)continue; //最短でなければSKIP
			for(int i = 0; i < G[node_id].size(); i++){
				Edge &e = G[node_id][i];
				if(e.capacity > 0 && dist[e.to] > dist[node_id]+e.cost+h[node_id]-h[e.to]){
					dist[e.to] = dist[node_id]+e.cost+h[node_id]-h[e.to];
					pre_node[e.to] = node_id;
					pre_edge[e.to] = i;
					Q.push(P(dist[e.to],e.to));
				}
			}
		}

		if(dist[sink] == BIG_NUM){
			//これ以上流せない
			return ret;
		}

		for(int node_id = 0; node_id < V; node_id++)h[node_id] += dist[node_id];

		//source-sink間最短路に沿って目いっぱい流す
		int tmp_flow = flow;
		for(int node_id = sink; node_id != source; node_id = pre_node[node_id]){
			tmp_flow = min(tmp_flow,G[pre_node[node_id]][pre_edge[node_id]].capacity);
		}
		flow -= tmp_flow;

		//printf("tmp_flow:%lld\n",tmp_flow);

		int mult = min(K,(H+W-1)-h[sink]);
		//printf("mult:%lld\n",mult);

		ret += tmp_flow*mult;

		//printf("ret:%lld\n",ret);

		for(int node_id = sink; node_id != source; node_id = pre_node[node_id]){
			Edge &e = G[pre_node[node_id]][pre_edge[node_id]];
			e.capacity -= tmp_flow;
			G[node_id][e.rev_index].capacity += tmp_flow;
		}
	}
	return ret;
}



int calc_maxi(int source,int sink,int not_row,int not_col){


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

		G[i].clear();
	}

	int CAP = H*W; //■1回の流れでは、なるべくコストを小さくするので、集められるだけの@が集まる

	add_edge(source,IN[getIndex(0,0)],CAP,0);
	add_edge(OUT[getIndex(H-1,W-1)],sink,CAP,0);

	for(ll row = 0; row < H; row++){
		for(ll col = 0; col < W; col++){
			if(input[row][col] == '#')continue;

			int id = getIndex(row,col);

			if((row == not_row && col == not_col) || (ANS[row][col] == '-')){ //■置かない

				add_edge(IN[id],OUT[id],CAP,1);

			}else{ //■置く

				add_edge(IN[id],OUT[id],1,0);
				add_edge(IN[id],OUT[id],CAP-1,1);
			}

			if(is_R(row,col)){
				int adj = getIndex(row,col+1);
				add_edge(OUT[id],IN[adj],CAP,0);
			}
			if(is_D(row,col)){
				int adj = getIndex(row+1,col);
				add_edge(OUT[id],IN[adj],CAP,0);
			}
		}
	}

	return min_cost_flow(source,sink,CAP);
}


void func(){

	for(int row = 0; row < H; row++){

		scanf("%s",input[row]);
		for(int col = 0; col < W; col++){

			ANS[row][col] = input[row][col];
		}
	}

	if(input[0][0] == '#' || input[H-1][W-1] == '#'){

		printf("0\n");
		outPut();
		return;
	}

	int index = 0;
	for(int row = 0; row < H; row++){
		for(int col = 0; col < W; col++){
			if(input[row][col] == '#')continue;

			ll id = getIndex(row,col);
			IN[id] = index++;
			OUT[id] = index++;
		}
	}

	int source = index,sink = index+1;
	V = index+2;

	//最大の設置個数を求める
	int maxi = calc_maxi(source,sink,-1,-1);
	printf("%d\n",maxi);
	//return;

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

			dp[row][col] = 0;
		}
	}

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

			if(input[row][col] == '#')continue;

			if(row-1 >= 0){

				dp[row][col] = max(dp[row][col],dp[row-1][col]);
			}
			if(col-1 >= 0){

				dp[row][col] = max(dp[row][col],dp[row][col-1]);
			}

			if(dp[row][col] == K){ //もう置けない

				ANS[row][col] = '-';
				continue;
			}

			ll tmp = calc_maxi(source,sink,row,col);
			//printf("row:%lld col:%lld tmp:%lld\n",row,col,tmp);

			if(tmp != maxi){ //■置かないと最大値を実現できない

				ANS[row][col] = '@';
				dp[row][col]++;

			}else{ //■置かなくても最大値を実現できる

				ANS[row][col] = '-'; //■"@"をなるべく後ろに置く
			}
		}
	}

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

			if(ANS[row][col] == '-'){

				ANS[row][col] = '.';
			}
		}
	}

	outPut();
	fflush(stdout);
}

int main(){

	while(true){

		scanf("%d %d %d",&H,&W,&K);
		if(H == 0 && W == 0 && K == 0)break;

		func();
	}

	return 0;
}
accuracy: 1
time_ms: 8000
memory_kb: 3556
final_rank: 4

code_snippet: #include <bits/stdc++.h>
using namespace std;


#include <algorithm>
#include <cassert>
#include <limits>
#include <queue>
#include <vector>


#include <algorithm>
#include <utility>
#include <vector>

namespace atcoder {
namespace internal {

template <class E> struct csr {
    std::vector<int> start;
    std::vector<E> elist;
    explicit csr(int n, const std::vector<std::pair<int, E>>& edges)
        : start(n + 1), elist(edges.size()) {
        for (auto e : edges) {
            start[e.first + 1]++;
        }
        for (int i = 1; i <= n; i++) {
            start[i] += start[i - 1];
        }
        auto counter = start;
        for (auto e : edges) {
            elist[counter[e.first]++] = e.second;
        }
    }
};

}  // namespace internal

}  // namespace atcoder


#include <vector>

namespace atcoder {

namespace internal {

template <class T> struct simple_queue {
    std::vector<T> payload;
    int pos = 0;
    void reserve(int n) { payload.reserve(n); }
    int size() const { return int(payload.size()) - pos; }
    bool empty() const { return pos == int(payload.size()); }
    void push(const T& t) { payload.push_back(t); }
    T& front() { return payload[pos]; }
    void clear() {
        payload.clear();
        pos = 0;
    }
    void pop() { pos++; }
};

}  // namespace internal

}  // namespace atcoder


namespace atcoder {

template <class Cap, class Cost> struct mcf_graph {
  public:
    mcf_graph() {}
    explicit mcf_graph(int n) : _n(n) {}

    int add_edge(int from, int to, Cap cap, Cost cost) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        assert(0 <= cap);
        assert(0 <= cost);
        int m = int(_edges.size());
        _edges.push_back({from, to, cap, 0, cost});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
        Cost cost;
    };

    edge get_edge(int i) {
        int m = int(_edges.size());
        assert(0 <= i && i < m);
        return _edges[i];
    }
    std::vector<edge> edges() { return _edges; }

    std::pair<Cap, Cost> flow(int s, int t) {
        return flow(s, t, std::numeric_limits<Cap>::max());
    }
    std::pair<Cap, Cost> flow(int s, int t, Cap flow_limit) {
        return slope(s, t, flow_limit).back();
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t) {
        return slope(s, t, std::numeric_limits<Cap>::max());
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);

        int m = int(_edges.size());
        std::vector<int> edge_idx(m);

        auto g = [&]() {
            std::vector<int> degree(_n), redge_idx(m);
            std::vector<std::pair<int, _edge>> elist;
            elist.reserve(2 * m);
            for (int i = 0; i < m; i++) {
                auto e = _edges[i];
                edge_idx[i] = degree[e.from]++;
                redge_idx[i] = degree[e.to]++;
                elist.push_back({e.from, {e.to, -1, e.cap - e.flow, e.cost}});
                elist.push_back({e.to, {e.from, -1, e.flow, -e.cost}});
            }
            auto _g = internal::csr<_edge>(_n, elist);
            for (int i = 0; i < m; i++) {
                auto e = _edges[i];
                edge_idx[i] += _g.start[e.from];
                redge_idx[i] += _g.start[e.to];
                _g.elist[edge_idx[i]].rev = redge_idx[i];
                _g.elist[redge_idx[i]].rev = edge_idx[i];
            }
            return _g;
        }();

        auto result = slope(g, s, t, flow_limit);

        for (int i = 0; i < m; i++) {
            auto e = g.elist[edge_idx[i]];
            _edges[i].flow = _edges[i].cap - e.cap;
        }

        return result;
    }

  private:
    int _n;
    std::vector<edge> _edges;

    struct _edge {
        int to, rev;
        Cap cap;
        Cost cost;
    };

    std::vector<std::pair<Cap, Cost>> slope(internal::csr<_edge>& g,
                                            int s,
                                            int t,
                                            Cap flow_limit) {

        std::vector<std::pair<Cost, Cost>> dual_dist(_n);
        std::vector<int> prev_e(_n);
        std::vector<bool> vis(_n);
        struct Q {
            Cost key;
            int to;
            bool operator<(Q r) const { return key > r.key; }
        };
        std::vector<int> que_min;
        std::vector<Q> que;
        auto dual_ref = [&]() {
            for (int i = 0; i < _n; i++) {
                dual_dist[i].second = std::numeric_limits<Cost>::max();
            }
            std::fill(vis.begin(), vis.end(), false);
            que_min.clear();
            que.clear();

            size_t heap_r = 0;

            dual_dist[s].second = 0;
            que_min.push_back(s);
            while (!que_min.empty() || !que.empty()) {
                int v;
                if (!que_min.empty()) {
                    v = que_min.back();
                    que_min.pop_back();
                } else {
                    while (heap_r < que.size()) {
                        heap_r++;
                        std::push_heap(que.begin(), que.begin() + heap_r);
                    }
                    v = que.front().to;
                    std::pop_heap(que.begin(), que.end());
                    que.pop_back();
                    heap_r--;
                }
                if (vis[v]) continue;
                vis[v] = true;
                if (v == t) break;
                Cost dual_v = dual_dist[v].first, dist_v = dual_dist[v].second;
                for (int i = g.start[v]; i < g.start[v + 1]; i++) {
                    auto e = g.elist[i];
                    if (!e.cap) continue;
                    Cost cost = e.cost - dual_dist[e.to].first + dual_v;
                    if (dual_dist[e.to].second - dist_v > cost) {
                        Cost dist_to = dist_v + cost;
                        dual_dist[e.to].second = dist_to;
                        prev_e[e.to] = e.rev;
                        if (dist_to == dist_v) {
                            que_min.push_back(e.to);
                        } else {
                            que.push_back(Q{dist_to, e.to});
                        }
                    }
                }
            }
            if (!vis[t]) {
                return false;
            }

            for (int v = 0; v < _n; v++) {
                if (!vis[v]) continue;
                dual_dist[v].first -= dual_dist[t].second - dual_dist[v].second;
            }
            return true;
        };
        Cap flow = 0;
        Cost cost = 0, prev_cost_per_flow = -1;
        std::vector<std::pair<Cap, Cost>> result = {{Cap(0), Cost(0)}};
        while (flow < flow_limit) {
            if (!dual_ref()) break;
            Cap c = flow_limit - flow;
            for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                c = std::min(c, g.elist[g.elist[prev_e[v]].rev].cap);
            }
            for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                auto& e = g.elist[prev_e[v]];
                e.cap += c;
                g.elist[e.rev].cap -= c;
            }
            Cost d = -dual_dist[s].first;
            flow += c;
            cost += c * d;
            if (prev_cost_per_flow == d) {
                result.pop_back();
            }
            result.push_back({flow, cost});
            prev_cost_per_flow = d;
        }
        return result;
    }
};

}  // namespace atcoder

using namespace atcoder;

int main() {
    int N, M, K;
    while (cin >> N >> M >> K, N) {
        vector<string> A(N);
        for (auto&& a : A) {
            cin >> a;
        }
        auto f = [&](int x, int y) { return x * M + y; };

        auto calc = [&](int xx, int yy) {
            mcf_graph<int, int> G(N * M * 2);
            for (int x = 0; x < N; x++) {
                for (int y = 0; y < M; y++) {
                    G.add_edge(f(x, y), f(x, y) + N * M, ((A[x][y] == '.' || A[x][y] == '@') && !(x == xx && y == yy)) ? 1 : 0, 0);
                }
            }
            for (int x = 0; x < N; x++) {
                for (int y = 0; y < M; y++) {
                    G.add_edge(f(x, y), f(x, y) + N * M, N + M, 1);
                }
            }
            for (int x = 0; x < N; x++) {
                for (int y = 0; y < M; y++) {
                    if (A[x][y] == '#')
                        continue;
                    if (x < N - 1 && A[x + 1][y] != '#')
                        G.add_edge(f(x, y) + N * M, f(x + 1, y), N + M, 0);
                    if (y < M - 1 && A[x][y + 1] != '#')
                        G.add_edge(f(x, y) + N * M, f(x, y + 1), N + M, 0);
                }
            }
            auto Slope = G.slope(0, 2 * N * M - 1);
            vector<int> costs = {0};
            for (int i = 0; i < (int)Slope.size() - 1; i++) {
                auto [cap_now, cost_now] = Slope[i];
                auto [cap_nxt, cost_nxt] = Slope[i + 1];
                int cost = (cost_nxt - cost_now) / (cap_nxt - cap_now);
                int score = N + M - 1 - cost;
                if (score == 0)
                    break;
                for (int j = cap_now + 1; j <= cap_nxt; j++) {
                    costs.emplace_back(score);
                }
            }
            int ret = 0;
            for (auto&& x : costs) {
                ret += min(K, x);
            }
            return ret;
        };

        int ans = calc(-1, -1);

        int cnt[30][30] = {};
        for (int xx = 0; xx < N; xx++) {
            for (int yy = 0; yy < M; yy++) {
                if (A[xx][yy] == '#')
                    continue;
                if (cnt[xx][yy] < K && calc(xx, yy) != ans) {
                    A[xx][yy] = '@';
                    cnt[xx][yy]++;
                } else
                    A[xx][yy] = '*';
                cnt[xx + 1][yy] = max(cnt[xx + 1][yy], cnt[xx][yy]);
                cnt[xx][yy + 1] = max(cnt[xx][yy + 1], cnt[xx][yy]);
            }
        }
        for (auto&& a : A) {
            for (auto&& c : a) {
                if (c == '*')
                    c = '.';
            }
        }

        cout << ans << endl;
        for (auto&& a : A) {
            cout << a << endl;
        }
    }
}
accuracy: 1
time_ms: 3490
memory_kb: 3628
final_rank: 1

code_snippet: #include <bits/stdc++.h>
using namespace std;


#include <algorithm>
#include <cassert>
#include <limits>
#include <queue>
#include <vector>


#include <algorithm>
#include <utility>
#include <vector>

namespace atcoder {
namespace internal {

template <class E> struct csr {
    std::vector<int> start;
    std::vector<E> elist;
    explicit csr(int n, const std::vector<std::pair<int, E>>& edges)
        : start(n + 1), elist(edges.size()) {
        for (auto e : edges) {
            start[e.first + 1]++;
        }
        for (int i = 1; i <= n; i++) {
            start[i] += start[i - 1];
        }
        auto counter = start;
        for (auto e : edges) {
            elist[counter[e.first]++] = e.second;
        }
    }
};

}  // namespace internal

}  // namespace atcoder


#include <vector>

namespace atcoder {

namespace internal {

template <class T> struct simple_queue {
    std::vector<T> payload;
    int pos = 0;
    void reserve(int n) { payload.reserve(n); }
    int size() const { return int(payload.size()) - pos; }
    bool empty() const { return pos == int(payload.size()); }
    void push(const T& t) { payload.push_back(t); }
    T& front() { return payload[pos]; }
    void clear() {
        payload.clear();
        pos = 0;
    }
    void pop() { pos++; }
};

}  // namespace internal

}  // namespace atcoder


namespace atcoder {

template <class Cap, class Cost> struct mcf_graph {
  public:
    mcf_graph() {}
    explicit mcf_graph(int n) : _n(n) {}

    int add_edge(int from, int to, Cap cap, Cost cost) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        assert(0 <= cap);
        assert(0 <= cost);
        int m = int(_edges.size());
        _edges.push_back({from, to, cap, 0, cost});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
        Cost cost;
    };

    edge get_edge(int i) {
        int m = int(_edges.size());
        assert(0 <= i && i < m);
        return _edges[i];
    }
    std::vector<edge> edges() { return _edges; }

    std::pair<Cap, Cost> flow(int s, int t) {
        return flow(s, t, std::numeric_limits<Cap>::max());
    }
    std::pair<Cap, Cost> flow(int s, int t, Cap flow_limit) {
        return slope(s, t, flow_limit).back();
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t) {
        return slope(s, t, std::numeric_limits<Cap>::max());
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);

        int m = int(_edges.size());
        std::vector<int> edge_idx(m);

        auto g = [&]() {
            std::vector<int> degree(_n), redge_idx(m);
            std::vector<std::pair<int, _edge>> elist;
            elist.reserve(2 * m);
            for (int i = 0; i < m; i++) {
                auto e = _edges[i];
                edge_idx[i] = degree[e.from]++;
                redge_idx[i] = degree[e.to]++;
                elist.push_back({e.from, {e.to, -1, e.cap - e.flow, e.cost}});
                elist.push_back({e.to, {e.from, -1, e.flow, -e.cost}});
            }
            auto _g = internal::csr<_edge>(_n, elist);
            for (int i = 0; i < m; i++) {
                auto e = _edges[i];
                edge_idx[i] += _g.start[e.from];
                redge_idx[i] += _g.start[e.to];
                _g.elist[edge_idx[i]].rev = redge_idx[i];
                _g.elist[redge_idx[i]].rev = edge_idx[i];
            }
            return _g;
        }();

        auto result = slope(g, s, t, flow_limit);

        for (int i = 0; i < m; i++) {
            auto e = g.elist[edge_idx[i]];
            _edges[i].flow = _edges[i].cap - e.cap;
        }

        return result;
    }

  private:
    int _n;
    std::vector<edge> _edges;

    struct _edge {
        int to, rev;
        Cap cap;
        Cost cost;
    };

    std::vector<std::pair<Cap, Cost>> slope(internal::csr<_edge>& g,
                                            int s,
                                            int t,
                                            Cap flow_limit) {

        std::vector<std::pair<Cost, Cost>> dual_dist(_n);
        std::vector<int> prev_e(_n);
        std::vector<bool> vis(_n);
        struct Q {
            Cost key;
            int to;
            bool operator<(Q r) const { return key > r.key; }
        };
        std::vector<int> que_min;
        std::vector<Q> que;
        auto dual_ref = [&]() {
            for (int i = 0; i < _n; i++) {
                dual_dist[i].second = std::numeric_limits<Cost>::max();
            }
            std::fill(vis.begin(), vis.end(), false);
            que_min.clear();
            que.clear();

            size_t heap_r = 0;

            dual_dist[s].second = 0;
            que_min.push_back(s);
            while (!que_min.empty() || !que.empty()) {
                int v;
                if (!que_min.empty()) {
                    v = que_min.back();
                    que_min.pop_back();
                } else {
                    while (heap_r < que.size()) {
                        heap_r++;
                        std::push_heap(que.begin(), que.begin() + heap_r);
                    }
                    v = que.front().to;
                    std::pop_heap(que.begin(), que.end());
                    que.pop_back();
                    heap_r--;
                }
                if (vis[v]) continue;
                vis[v] = true;
                if (v == t) break;
                Cost dual_v = dual_dist[v].first, dist_v = dual_dist[v].second;
                for (int i = g.start[v]; i < g.start[v + 1]; i++) {
                    auto e = g.elist[i];
                    if (!e.cap) continue;
                    Cost cost = e.cost - dual_dist[e.to].first + dual_v;
                    if (dual_dist[e.to].second - dist_v > cost) {
                        Cost dist_to = dist_v + cost;
                        dual_dist[e.to].second = dist_to;
                        prev_e[e.to] = e.rev;
                        if (dist_to == dist_v) {
                            que_min.push_back(e.to);
                        } else {
                            que.push_back(Q{dist_to, e.to});
                        }
                    }
                }
            }
            if (!vis[t]) {
                return false;
            }

            for (int v = 0; v < _n; v++) {
                if (!vis[v]) continue;
                dual_dist[v].first -= dual_dist[t].second - dual_dist[v].second;
            }
            return true;
        };
        Cap flow = 0;
        Cost cost = 0, prev_cost_per_flow = -1;
        std::vector<std::pair<Cap, Cost>> result = {{Cap(0), Cost(0)}};
        while (flow < flow_limit) {
            if (!dual_ref()) break;
            Cap c = flow_limit - flow;
            for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                c = std::min(c, g.elist[g.elist[prev_e[v]].rev].cap);
            }
            for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                auto& e = g.elist[prev_e[v]];
                e.cap += c;
                g.elist[e.rev].cap -= c;
            }
            Cost d = -dual_dist[s].first;
            flow += c;
            cost += c * d;
            if (prev_cost_per_flow == d) {
                result.pop_back();
            }
            result.push_back({flow, cost});
            prev_cost_per_flow = d;
        }
        return result;
    }
};

}  // namespace atcoder

using namespace atcoder;

int main() {
    int N, M, K;
    while (cin >> N >> M >> K, N) {
        vector<string> A(N);
        for (auto&& a : A) {
            cin >> a;
        }
        auto f = [&](int x, int y) { return x * M + y; };

        auto calc = [&](int xx, int yy) {
            mcf_graph<int, int> G(N * M * 2);
            for (int x = 0; x < N; x++) {
                for (int y = 0; y < M; y++) {
                    G.add_edge(f(x, y), f(x, y) + N * M, ((A[x][y] == '.' || A[x][y] == '@') && !(x == xx && y == yy)) ? 1 : 0, 1);
                }
            }
            for (int x = 0; x < N; x++) {
                for (int y = 0; y < M; y++) {
                    G.add_edge(f(x, y), f(x, y) + N * M, N + M, 100);
                }
            }
            for (int x = 0; x < N; x++) {
                for (int y = 0; y < M; y++) {
                    if (A[x][y] == '#')
                        continue;
                    if (x < N - 1 && A[x + 1][y] != '#')
                        G.add_edge(f(x, y) + N * M, f(x + 1, y), N + M, 0);
                    if (y < M - 1 && A[x][y + 1] != '#')
                        G.add_edge(f(x, y) + N * M, f(x, y + 1), N + M, 0);
                }
            }
            auto Slope = G.slope(0, 2 * N * M - 1);
            vector<int> costs = {0};
            for (int i = 0; i < (int)Slope.size() - 1; i++) {
                auto [cap_now, cost_now] = Slope[i];
                auto [cap_nxt, cost_nxt] = Slope[i + 1];
                int cost = (cost_nxt - cost_now) / (cap_nxt - cap_now);
                if (cost % 100 == 0)
                    break;
                for (int j = cap_now + 1; j <= cap_nxt; j++) {
                    costs.emplace_back(cost % 100);
                }
            }
            int ret = 0;
            for (auto&& x : costs) {
                ret += min(K, x);
            }
            return ret;
        };

        int ans = calc(-1, -1);

        for (int xx = 0; xx < N; xx++) {
            for (int yy = 0; yy < M; yy++) {
                if (A[xx][yy] == '#')
                    continue;
                if (calc(xx, yy) != ans)
                    A[xx][yy] = '@';
                else
                    A[xx][yy] = '*';
            }
        }
        for (auto&& a : A) {
            for (auto&& c : a) {
                if (c == '*')
                    c = '.';
            }
        }

        cout << ans << endl;
        for (auto&& a : A) {
            cout << a << endl;
        }
    }
}
accuracy: 1
time_ms: 4260
memory_kb: 3628
final_rank: 3