#include <iostream>
#include <vector>
#include "stdio.h"
#include "assert.h"

using namespace std;

const int kSize = 9;

class Row;
class Col;

class Cell {
 public:
  Row *row;
  Col *col;
  void setValue(int v);
  int getValue() const;
  int solution;
 private:
  int value;
};

class Constraints {
 public:
  Constraints() {
    for (int i=0;i<kSize;++i) data[i] = -1;
  }
  Constraints(const Constraints &c) {
    for (int i=0;i<kSize;++i) data[i] = c.data[i];
  }
  
  // -1 means unknown (can be anything)
  // 0 means all solutions seen have been 0, but 1 might exist
  // 1 means all solutions seen have been 1, but 0 might exist
  //
  // value 4 should never be used (haven't written code for it yet)
  // values 2 and 3 are only used for when the solution list

  /*
     A Constraints object represents one of four things:
       (1) a "solution";
             (only 0s and 1s allowed)
       (2) a "partial solution";
             (only 0s, 1s, and -1s allowed)
       (3) a "complete aggregate of solutions"
             (only 0s, 1s, and -1s allowed)
  */

  int data[kSize];

  static int allowFor(const int oldv, const int newv) {
    assert(newv >= 0);
    if (oldv == 0 && newv == 1) {
      return -1;
    } else if (oldv == 1 && newv == 0) {
      return -1;
    } else {
      return oldv;
    }
  }

  void allowFor(const Constraints solution) {
    for (int i=0; i<kSize; ++i) {
      data[i] = allowFor(data[i],solution.data[i]);
    }
  }
  
  bool complete() const {
    for (int i=0; i<kSize; ++i) {
      if (data[i] == -1) return false;
    }
    return true;
  }

  static Constraints aggregate(const vector<Constraints>* solutions) { 
    Constraints answer;
    for (vector<Constraints>::const_iterator it = solutions->begin();
         it != solutions->end(); ++it) {
      if (it == solutions->begin()) 
        answer = *it;
      else 
        answer.allowFor(*it);
    }
    return answer;
  }

  static bool fits(const Constraints c, const Constraints s) {
    for (int i=0; i<kSize; i++) {
      assert (s.data[i] >= 0);
      if (c.data[i] == 0 && s.data[i] == 1) return false;
      if (c.data[i] == 1 && s.data[i] == 0) return false;
    }
    return true;
  }
};

// A solution is just a constraint with only 0s and 1s.

class Solvable {
 public:
  Solvable(): sum(0), tainted(false) {}

  Cell* cells[kSize];

  virtual int WeightAt(const int pos) const {}
  virtual int ValAt(const int pos) const {}
  int value;  // represents the "correct" value.
  int weight_order[kSize]; 
  // WeightAt(weight_order(a)) >= WeightAt(weight_order(b)) iff a < b

  vector<Constraints> sols;
  Constraints summary; // what we know for sure.
  Constraints possible; // what we know is possible.
  int sum;

  bool tainted;
  void taint() {
    tainted = true;
  }

  void swap_weight_values(const int v1, const int v2) {
    int temp = weight_order[v1];
    weight_order[v1] = weight_order[v2];
    weight_order[v2] = temp;
  }

  void sink_sort_weight_order(const int min, const int max) {
    for (int j=max;j>=min+1;--j) {
      for (int i=min;i<j;++i) {
        if (WeightAt(weight_order[i]) < WeightAt(weight_order[i+1])) {
          swap_weight_values(i,i+1);
        }
      }
    }
  }

  void sort_weight_order(const int min, const int max) {
    if (max - min <= 4)
      sink_sort_weight_order(min,max);
    else {
      // max will be the pivot value
      int pivotvalue = WeightAt(weight_order[max]);
      int stored = min;
      for (int i=min; i<max; ++i) {
        if (WeightAt(weight_order[i]) > pivotvalue) {
          swap_weight_values(i,stored);
          stored++;
        }
      }
      swap_weight_values(max,stored);
      sort_weight_order(min,stored-1);
      sort_weight_order(stored+1,max);
    }
  }

  void seed() {
    // initializes some cached data
    // weight_order
    for (int i=0;i<kSize;++i) {
      weight_order[i] = i;
    }
    sort_weight_order(0,kSize-1);
  }
  
  int sumof(const Constraints possible) const {
    int answer = 0;
    for (int i=0; i<kSize; ++i) {
      if (possible.data[i] != -1) {
        answer += possible.data[i] * WeightAt(i);
      }
    }
    return answer;
  }

  bool fits(const Constraints solution) const {
    // returns true if the solution works.
    int vl = 0;
    for (int i=0; i<kSize; ++i) {
      assert(solution.data[i] != -1);
      vl += solution.data[i] * WeightAt(i);
      if (vl > value) return false;
    }
    return (vl == value);
  }
  
  bool FindSolution(Constraints* partial_solution, 
                    const int layer, 
                    const int sum) const {
    // Finds ONE solution that works, and returns true if found.
    // If there is at least one solution, this will find it.

    // Note that partial_solution is mutable!

    // we assert that all weight_order[i] with i < layer are alreay
    // set in the partial solution (i.e., not -1);

    if (layer >= kSize) {
      return (sum == value);
    }

    const int index = weight_order[layer];
    int * const cur_value = &(partial_solution->data[index]);
    const int weight = WeightAt(index);
 
    if (*cur_value == 0) {
      return FindSolution(partial_solution, layer+1, sum);
    } else if (*cur_value == 1) {
      if (sum + weight > value) return false;
      return FindSolution(partial_solution, layer+1, sum+weight);
    } else {
      assert (*cur_value == -1);
      if (sum + weight <= value) {
        *cur_value = 1; // try 1.
        if (FindSolution(partial_solution, layer+1, sum+weight)) {
          return true;
        }
      }
      *cur_value = 0; // try 0.
      if (FindSolution(partial_solution, layer+1, sum)) {
        return true;
      }
      // nothing works, revert.
      *cur_value = -1;
      return false;
    }

    return false;
  }

  void set(const Constraints c) {
    for (int i=0; i<kSize; ++i) {
      cells[i]->setValue(c.data[i]);
    }
  }
  Constraints getExternalConstraints() {
    Constraints c;
    for (int i=0; i<kSize; ++i) {
      c.data[i] = cells[i]->getValue();
    }
    return c;
  }

  void untaint() {
    // makes sure that:
    //   sols doesn't contradict external constraints;
    //   filled cells represents both external and sols/complete.
    summary = getExternalConstraints();
    sum = sumof(summary);
    for (vector<Constraints>::iterator it = sols.begin();
         it != sols.end(); ) {
      if (!Constraints::fits(summary,*it)) {
        it = sols.erase(it);
      } else {
        ++it;
      }
    }
    possible = Constraints::aggregate(&sols);
    tainted = false;
  }

  int candidate() const {
    for (int i=0; i<kSize; ++i) {
      if (possible.data[i] != -1 && summary.data[i] == -1)
        return i;
    }
    return -1;
  }

  bool makeProgress() {
    if (!tainted) {
      cout << " no progress; not tainted. \n";
      return false;
    }
    untaint();
    if (sum == value) {
      // set the unknowns to zero.
      bool found_unknown = false;
      for (int i=0;i<kSize;++i) {
        if (summary.data[i] == -1) {
          found_unknown = true;
          cells[i]->setValue(0);
        }
      }
      if (found_unknown) {
        cout << " filled in zeroes. \n";
        return true;
      } else {
        cout << " no progress; already solved. \n";
        return false;
      }
    }
    if (sols.size() == 0) {
      Constraints c(summary);
      bool found = FindSolution(&c, 0, 0);
      assert(found);
      sols.push_back(c);
      cout << " added a solution. (";
//      for (int i=0;i<kSize;++i) {
//        cout << c.data[i] << ",";
//      }
      cout << ")\n";
      taint();
      return true;
    } else {
      // find a candidate.
      int can = candidate();
      if (can == -1) {
        cout << " no progress; all bits possible. \n";
        return false;
      } else {
        cout << " looking at position " << can << " being "
             << (1-possible.data[can]) << "...\n    ";
        Constraints c(summary);
        // try the alternative;
        c.data[can] = 1 - possible.data[can];
        bool found = FindSolution(&c, 0, 0);
        if (found) {
          sols.push_back(c);
          cout << " added a solution. (";
//          for (int i=0;i<kSize;++i) {
//            cout << c.data[i] << ",";
//          }
          cout << ")\n";
          taint();
          return true;
        } else {
          // a ha!  can is forced.
          cells[can]->setValue(possible.data[can]);
          cout << " forced to be " << possible.data[can] << "\n";
          return true;
        }
      }
    }
  }

};

class Row : public Solvable {
 public:
  int weight;
  void CalcValue();
  int WeightAt(const int pos) const;
  int ValAt(const int pos) const;

  void assignFirstSolution() {
    Constraints c;
    if (FindSolution(&c,0,0)) {
      set(c);
    }
  }
};

class Col : public Solvable {
 public:
  int weight;
  void CalcValue();
  int WeightAt(const int pos) const;
  int ValAt(const int pos) const;
};

  int Row::WeightAt(const int pos) const { return cells[pos]->col->weight; }
  int Row::ValAt(const int pos) const { return cells[pos]->getValue(); }
  int Col::WeightAt(const int pos) const { return cells[pos]->row->weight; }
  int Col::ValAt(const int pos) const { return cells[pos]->getValue(); }
  void Row::CalcValue() {
    value = 0;
    for (int i=0; i<kSize; ++i) {
      value += (cells[i]->solution) * WeightAt(i);
    }
  }
  void Col::CalcValue() {
    value = 0;
    for (int i=0; i<kSize; ++i) {
      value += (cells[i]->solution) * WeightAt(i);
    }
  }

class Grid {
 public: 
  Grid() {
    for (int r=0; r<kSize; ++r) {
      cin >> rows[r].weight;
    }
    for (int c=0; c<kSize; ++c) {
      cin >> cols[c].weight;
    }
    for (int r=0; r<kSize; ++r) {
      for (int c=0; c<kSize; ++c) {
        rows[r].cells[c] = &cells[r*kSize+c];
        cols[c].cells[r] = &cells[r*kSize+c];
        cells[r*kSize+c].row = &rows[r];
        cells[r*kSize+c].col = &cols[c];
        cells[r*kSize+c].setValue(-1);
        cin >> cells[r*kSize+c].solution;
      }
    }
    for (int i=0; i<kSize; ++i) {
      rows[i].CalcValue();
      rows[i].seed();
      cols[i].CalcValue();
      cols[i].seed();
    }
  }
  Cell cells[kSize*kSize];
  Row rows[kSize];
  Col cols[kSize];

  void Print() {
    cout << "          ";
    for (int c=0; c<kSize; ++c) {
      printf("%3d ", cols[c].value);
    }
    cout << "\n";
    cout << "          ";
    for (int c=0; c<kSize; ++c) {
      printf("%3d ", cols[c].value - cols[c].sum);
    }
    cout << "\n";
    cout << "\n";
    for (int r=0; r<kSize; ++r) {
      printf("%3d ", rows[r].value);
      printf("%3d   ", rows[r].value - rows[r].sum);
      for (int c=0; c<kSize; ++c) {
        if (cells[r*kSize+c].getValue() == -1)
          printf("  ? ");
        else
          printf("%3d ",cells[r*kSize+c].getValue());
      }
      printf("  %3d", rows[r].weight);
      cout << "\n";
    }
    cout << "\n";
//    cout << "      ";
//    for (int c=0; c<kSize; ++c) {
//      printf("%3d ", cols[c].weight);
//    }
//    cout << "\n";
//    cout << "      ";
//    for (int c=0; c<kSize; ++c) {
//      printf("%3d ", cols[c].value);
//    }
//    cout << "\n";
//    cout << "\n";
/*
    for (int r=0; r<kSize; ++r) {
      printf("%3d   ", rows[r].value);
      for (int c=0; c<kSize; ++c) {
        printf("%3d ",cells[r*kSize+c].solution);
      }
      printf("  %3d", rows[r].weight);
      cout << "\n";
    }
    cout << "\n";
*/
    cout << "          ";
    for (int c=0; c<kSize; ++c) {
      printf("%3d ", cols[c].weight);
    }
    cout << "\n";
  }
};

void Cell::setValue(int v) {
  row->taint();
  col->taint();
  value = v;
}
int Cell::getValue() const {
  return value;
}

int main() {
  Grid g;
  g.Print();
  for (int pass=0; ; pass++) {
    cout << "Trying pass " << pass << "\n";
    bool madeProgress = false;
    for (int i=0; i<kSize; ++i) {
      cout << "  Row " << i;
      if (g.rows[i].makeProgress())
        madeProgress = true;
    }
    for (int i=0; i<kSize; ++i) {
      cout << "  Col " << i;
      if (g.cols[i].makeProgress())
        madeProgress = true;
    }
    g.Print();
    if (!madeProgress) break;
  }
}
