#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<vector>
#include<set>
#include<map>
#define int long long 
#define MOD 1000000007
#define br break
#define PI 3.1415926535
#define INF 1e18 
#define ve vector
#define mp make_pair
#define pb push_back
#define pf push_front
#define ppb pop_back
#define ppf pop_front
#define mxe(v) *max_element(v.begin(),v.end())
#define mne(v) *min_element(v.begin(),v.end())
#define bs binary_search

#define ub upper_bound
#define FAST ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define rep(g, i, n) for (int g = i; g < n; g++)
#define rev(g, n, i) for (ll g = n - 1; g >= i; g--)
using namespace std;

// Uncomment for using Graph Theory Approaches


// vector<int> adj[300005]; // adjacency list representation
//queue<int> q;
// bool used[300005]={false};
// int d[300005], p[300005];



// void bfs(int s,int n)
// {
//     rep(i,0,n)
//         used[i]=false,d[i]=INT_MAX,p[i]=-1;
//     q.push(s);
//     used[s] = true;
//     p[s] = -1;
//     while (!q.empty()) 
//     {
//         int v = q.front();
//         q.pop();
//         for (int u : adj[v]) 
//         {
//             if (!used[u]) 
//             {
//                 used[u] = true;
//                 q.push(u);
//                 d[u] = d[v] + 1;
//                 p[u] = v;
//             }
//         }
//     }
// }
// printBfs(int s, int u)
// {
//     if (!used[u]) 
//     {
//         cout << "No path!";
//     } 
//     else 
//     {
//         vector<int> path;
//         for (int v = u; v != -1; v = p[v])
//             path.push_back(v);
//         reverse(path.begin(), path.end());
//         cout << "Path: ";
//         for (int v : path)
//             cout << v << " ";
//     }
// }
// void dfs(int v) {
//     visited[v] = true;
//     for (int u : adj[v]) {
//         if (!visited[u])
//             dfs(u);
//     }
// }




//If we have to restore and display the shortest path from the source to some vertex u, it can be done in the following manner:

//void fastscan(int &number)// For fast input
//{ 
//    //variable to indicate sign of input number 
//    bool negative = false; 
//    register int c; 
//  
//    number = 0; 
//  
//    // extract current character from buffer 
//    c = getchar(); 
//    if (c=='-') 
//    { 
//        // number is negative 
//        negative = true; 
//  
//        // extract the next character from the buffer 
//        c = getchar(); 
//    } 
//  
//    // Keep on extracting characters if they are integers 
//    // i.e ASCII Value lies from '0'(48) to '9' (57) 
//    for (; (c>47 && c<58); c=getchar()) 
//        number = number *10 + c - 48; 
//  
//    // if scanned input has a negative sign, negate the 
//    // value of the input number 
//    if (negative) 
//        number *= -1; 
//}
//int fast_exp(int base, int exp) {
//    int res=1;
//    while(exp>0) {
//       if(exp%2==1) res=(res*base)%MOD;
//       base=(base*base)%MOD;
//       exp/=2;
//    }
//    return res%MOD;
//}
//void SieveOfEratosthenes(int n) 
//{ 
//    bool prime[n+1]; 
//    memset(prime, true, sizeof(prime)); 
//  
//    for (int p=2; p*p<=n; p++) 
//    { 
//        if (prime[p] == true) 
//        { 
//            for (int i=p*p; i<=n; i += p) 
//                prime[i] = false; 
//        } 
//    } 
//    for (int p=2; p<=n; p++) 
//       if (prime[p]) 
//          cout << p << " "; 
//}
//void find(int n) // Find powers of prime factorization of number
//{ 
//    bool prime[n+1]; 
//  memset(prime, true, sizeof(prime)); 
//  for (int p=2; p*p<=n; p++) 
//    { 
//        if (prime[p] == true) 
//        { 
//            for (int i=p*p; i<=n; i += p) 
//                prime[i] = false; 
//        } 
//    }
//    vector<int> powers;
//    for (int p=2; p<=n; p++) 
//    {
//      if (prime[p] && n%p==0)
//      {
//          int count=0;
//          while(n%p==0 && n!=0)
//          {
//              n=n/p;
//              count++;
//          }
//          powers.pb(count);
//      }
//  }
//    for(int i=0;i<powers.size();i++)  
//      cout<<powers[i]<<" "; 
// }
// int binarySearch(int arr[], int l, int r, int x) 
// { 
//     while (l <= r) 
//     { 
//         int m = l + (r - l) / 2;  
//         if (arr[m] == x) 
//             return m;  
//         if (arr[m] < x) 
//             l = m + 1; 
//         else
//             r = m - 1; 
//     } 
//     return -1; 
// } 
//===================================================================================================================
// THE FIRST PRINCIPLE, IS THAT YOU MUST NOT FOOL YOURSELF, AND YOU ARE THE EASIEST PERSON TO FOOL- Richard Feynman.
// ******************************************************************************************************************
// CODE BEGINS HERE:
int countSetBits(int n) 
{ 
    int count = 0; 
    while (n) 
	{ 
        count += n & 1; 
        n >>= 1; 
    } 
    return count; 
} 
void solve()
{
  	int n;
  	cin>>n;
  	// Since constraints are small, we can use Bitmasks
  	int match[n][n];
  	rep(i,0,n)
  	{
  		rep(j,0,n)
  		{
  			cin>>match[i][j];
  		}
  	}
  	int dp[(1<<n)]={0};
  	// MASK IS REPRESENTED BY WOMEN
  	dp[0]=1;// Match no one
  	
	for(int mask=0;mask<(1<<n)-1;mask++)// Iterate over mask: every possible combination of women
	{
		// Number of bits set in mask==a
		int a=countSetBits(mask);
		for(int b=0;b<n;b++)// Checking bits
		{
			if(match[a][b]==1 && !(mask&(1<<b)))// Man a and Woman b can be matched, and in Mask, bth bit shouldn't be set
			{
				int m2=mask^(1<<b);// Change it to 1
				dp[m2]=(dp[m2]+dp[mask])%MOD;
			}

		}
	}
  	cout<<dp[(1<<n)-1];// 2^N-1 will have all bits in mask set to one, meaning that all women have been matched






}
signed main() 
{ 
//#ifndef ONLINE_JUDGE
    //  freopen("input.txt","r",stdin);
    //  freopen("output.txt","w",stdout);
//#endif
    FAST;
    // int t;
    // cin>>t;
    // while(t--)
    	solve();
    return 0; 
} 





// sometimes I believe compiler ignores all my comments

Measured memory_bytes: 16640