//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <string>
#include <complex>
using namespace std;
typedef long long LL;
const int MAXN = 45;
const int MAXM = 120;
const int inf = (1<<30);
const int maxn = 100011;
const int maxm = 3200011;
int n,m,S,T,cook_cnt,tot,t[45][150],head,tail;//n表示菜品,m表示厨师数量
int num[MAXN],first[maxn],ecnt,ans,dis[maxn],pre[maxn],ppre[maxn],dui[maxn*30];
bool in[maxn];
//感觉很像我以前做的一道省选题,叫修车来着...正着不好做,考虑算每次的贡献,倒数第k次对答案的总贡献就是k*tim
//但是这题的数据范围很大,我们需要优化。考虑因为假设一个厨师做的倒数第k道菜尚未决策,那么没有必要考虑倒数第k+1次。所以我们动态加边,当倒数第k道菜做完才加入倒数第k+1道菜的位置,每次修改增广路时很容易得到。
//这样的话,复杂度是可以保证的。因为每次图的点数都是n+m,边数是n*m...
struct edge{
int to,f,w,next;
}e[maxm];
inline void link(int x,int y,int f,int z){
e[++ecnt].next=first[x]; first[x]=ecnt; e[ecnt].to=y; e[ecnt].f=f; e[ecnt].w=z;
e[++ecnt].next=first[y]; first[y]=ecnt; e[ecnt].to=x; e[ecnt].f=0; e[ecnt].w=-z;
}
inline int getint(){
int w=0,q=0; char c=getchar(); while((c<‘0‘||c>‘9‘) && c!=‘-‘) c=getchar();
if(c==‘-‘) q=1,c=getchar(); while (c>=‘0‘&&c<=‘9‘) w=w*10+c-‘0‘,c=getchar(); return q?-w:w;
}
inline bool SPFA(){
for(int i=0;i<=T;i++) dis[i]=inf,in[i]=false,pre[i]=ppre[i]=-1;
head=tail=0; dui[++tail]=0; in[0]=true; dis[0]=0; int u;
while(head<tail) {
u=dui[++head]; in[u]=false;
for(int i=first[u];i;i=e[i].next) {
if(e[i].f==0) continue; int v=e[i].to;
if(dis[v]>dis[u]+e[i].w) {
dis[v]=dis[u]+e[i].w;
pre[v]=u; ppre[v]=i;
if(!in[v]) { in[v]=true; dui[++tail]=v; }
}
}
}
if(dis[T]==inf) return false;
return true;
}
inline void solve(){
int u=T,ff=inf;
for(;u!=S;u=pre[u]) ff=min(ff,e[ppre[u]].f);
for(u=T;;u=pre[u]) { ans+=ff*e[ppre[u]].w,e[ppre[u]].f-=ff,e[ppre[u]^1].f+=ff; if(pre[u]==S) break; }
int cook,cc; cook=(u-1)/tot+1; cc=(u-1)%tot+1; if(cc==tot) return ;
cc++; u++;
link(S,u,1,0);
for(int i=1;i<=n;i++) link(u,cook_cnt+i,1,t[i][cook]*cc);
}
inline void work(){
n=getint(); m=getint(); for(int i=1;i<=n;i++) num[i]=getint(),tot+=num[i];//tot表示需要做的菜的总数
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) t[i][j]=getint();
cook_cnt=m*tot;//厨师拆成点的总数
S=0; T=cook_cnt+n+1; ecnt=1;
for(int i=1;i<=n;i++) link(cook_cnt+i,T,num[i],0);
for(int i=1;i<=m;i++) link(S,(i-1)*tot+1,1,0);
for(int i=1;i<=m;i++)//从每个厨师的第一个点出发连边
for(int j=1;j<=n;j++)
link((i-1)*tot+1,cook_cnt+j,1,t[j][i]);
while(SPFA())
solve();
printf("%d",ans);
}
int main()
{
work();
return 0;
}