Problem:
図画工作
イヅア大学附属小学校は日本有数の競技プログラマー養成校として有名です。この学校の教員は幅広い
アルゴリズムの知識を持ち、日々それを活用しています。教員であるあなたは、今年は図画工作の授業
を担当することになりました。この授業では、児童全員がそれぞれ１年間で一つの課題を完成させることになっています。授業の概要は以下のとおりです。
１年間で授業は D 回（同じ日に2 回以上授業はない）あり、その全てが課題制作に充てられる。
制作する課題は M 種類用意されている。
それぞれの児童に、M 種類の中から課題を１つずつ割り当てる。
児童は N 人であり、N 人それぞれに異なる課題が割り当てられる。
児童は、K 種類ある部品のうちいくつかの種類の部品を使用して課題を完成させます。課題の制作の概要は以下のとおりです。
課題ごとに、使用すべき部品の種類と数はあらかじめ決められている。
課題が完成するまでに使用する部品の種類と数は、その課題で使用すべき部品の種類と数に、過不足なく一致していなければならない。
異なる課題でも、使われる部品の種類と数がすべて同じ場合もある。
どの課題も、同じ種類の部品は２個までしか使用できない。
部品を使用する順序は課題の完成に影響を与えない。
いくつかの部品が入っている袋が事前に P 個用意されている。ただし、異なる袋でも、入っている部品の種類と数がすべて同じ場合もある。
教員は、児童１人につき袋を１つだけ渡すことができる（袋を渡さない児童がいてもよい）。
２人以上の児童に同じ袋を渡すことはできない（反対に、誰にも渡されない袋があってもよい）。
袋を渡された児童は、袋の中に入っている部品をすべて、自分が制作する課題に使わなければならない。
袋に入っている部品以外で課題に使用する部品は、別途購入する必要があります。部品の購入に関する条件は以下のとおりです。
部品は授業の日だけ購入でき、その日にしか使えない。
それぞれの課題について、１回の授業でＬ個までの部品を購入することができる。
部品の種類によって価格が設定されており、購入する日によって価格が変動する。ただし、どの種類も品切れになることはない。
あなたは、このような条件下で、授業にかかる経費をなるべく抑えたいと考えています。そこで、児童全員の部品購入費の合計の最小値を計算するプログラムを作成することにしました。
入力
入力は複数のデータセットからなる。入力の終わりはゼロ３つの行で示される。各データセットは以下の形式で与えられる。
D K L
c
1,1
c
1,2
... c
1,K
c
2,1
c
2,2
... c
2,K
:
c
D,1
c
D,2
... c
D,K
M N P
r
1,1
r
1,2
... r
1,K
r
2,1
r
2,2
... r
2,K
:
r
M,1
r
M,2
... r
M,K
t
1,1
t
1,2
... t
1,K
t
2,1
t
2,2
... t
2,K
:
t
P,1
t
P,2
... t
P,K
各行で与えられる数値は１つの空白で区切られている。
D（1 ≤ D ≤ 8）は授業の回数、K（1 ≤ K ≤ 8）は部品の種類の数、L（1 ≤ L ≤ 8）は１日に購入することができる部品の数を示す。c
d,k
（1 ≤ c
d,k
≤ 100）は d 日目の部品k の価格を示す。
M（1 ≤ M ≤ 200）は課題の種類の数、N（1 ≤ N ≤ M）は生徒の人数、P（1 ≤ P ≤ 200）は袋の数を示す。
r
m,k
（0 ≤ r
m,k
≤ 2）は課題 m に必要な部品 k の個数、t
p,k
（0 ≤ t
p,k
≤ 2）は袋 p に入っている部品 k の個数を示す。
部品を１つも必要としない課題や、空の袋は与えられないものとする。
データセットの数は20 を超えない。
出力
データセットごとに、生徒全員の部品購入費の合計の最小値を１行に出力する。Ｎ種類の作品を完成させることができない場合は -1 を出力する。
入力例
2 2 1
5 3
2 2
3 3 1
2 0
1 2
2 2
1 1
2 2 1
5 3
2 2
3 2 1
2 0
1 2
2 2
1 1
2 2 2
5 3
2 2
3 2 1
2 0
1 2
2 2
1 1
4 3 1
2 2 1
3 2 2
2 3 3
1 2 2
5 4 3
1 1 0
1 0 1
1 0 2
1 1 2
2 2 2
1 0 1
2 0 2
1 1 1
0 0 0
出力例
-1
9
6
7

Submitted solutions:

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <set>
using namespace std;
typedef long long ll;
#define rep(i, n) for(int i = 0; i < (n); i++)

template<class T>
using vi = vector<T>;

template<class T>
using vii = vector<vi<T>>;

template<class T>
using viii = vector<vii<T>>;


struct MCF {
private:
    const long long inf = 1e18;
    vector<long long> dist;       //最短距離(費用)
    vector<int> pv, pe;     //直前の頂点、辺    

public:
    struct edge {
        int to, rev; long long cap, cost;
        edge(int t = 0, int r = 0, long long ca = 0, long long co = 0)
            : to(t), rev(r), cap(ca), cost(co) {}
    };

    struct pii {
        long long d; int v; //最短距離(費用)、頂点番号
        pii(long long d_ = 0, int v_ = 0) : d(d_), v(v_) {}

        bool operator<(const pii& other) const {
            if (d != other.d) return d < other.d;
            return v < other.v;
        }

        bool operator>(const pii& other) const {
            if (d != other.d) return d > other.d;
            return v > other.v;
        }
    };

    int n;
    vector<vector<edge>> to;
    vector<long long> pot;        //ポテンシャル

    MCF(int n_ = 1) : n(n_), to(n_), pot(n_), dist(n_), pv(n_), pe(n_) {}

    void addedge(int u, int v, long long cap, long long cost) {
        int su = (int)to[u].size();
        int sv = (int)to[v].size();
        to[u].push_back({ v, sv, cap, cost });
        to[v].push_back({ u, su, 0, -cost });
    }

    //s->tへ流量fの最小費用流、流せないときは-1
    long long mincostflow(int s, int t, long long f) {
        long long res = 0;
        pot.assign(n, 0);
        while (f) {
            priority_queue<pii, vector<pii>, greater<pii>> pq;
            dist.assign(n, inf);
            dist[s] = 0;
            pq.push({ 0, s });
            while (!pq.empty()) {
                pii elem = pq.top();
                pq.pop();
                if (dist[elem.v] < elem.d) continue;
                int sv = (int)to[elem.v].size();
                for (int i = 0; i < sv; i++) {
                    edge& nv = to[elem.v][i];
                    if (nv.cap == 0 || dist[nv.to] + pot[nv.to] <= dist[elem.v] + pot[elem.v] + nv.cost) continue;
                    dist[nv.to] = dist[elem.v] + pot[elem.v] - pot[nv.to] + nv.cost;
                    pv[nv.to] = elem.v;
                    pe[nv.to] = i;
                    pq.push({ dist[nv.to], nv.to });
                }
            }

            if (dist[t] == inf) return -1; //流せる最小費用流が存在しない

            for (int v = 0; v < n; v++) pot[v] += dist[v];

            long long nf = f;
            for (int v = t; v != s; v = pv[v]) nf = min(nf, to[pv[v]][pe[v]].cap);
            f -= nf;
            res += nf * (pot[t] - pot[s]); //pot[s] = 0で不要だがポテンシャルを明確化する
            for (int v = t; v != s; v = pv[v]) {
                edge& nv = to[pv[v]][pe[v]];
                nv.cap -= nf;
                to[v][nv.rev].cap += nf;
            }
        }
        return res;
    }
};

void chmin(ll& x, ll y) { x = min(x, y); }

int main()
{
    vi<ll> ans;

    vi<int> pow3(10);
    pow3[0] = 1;
    rep(i, 9) pow3[i + 1] = pow3[i] * 3;
    const ll inf = 1e18;

    //d:授業数、k:部品種類数、l:1日に購入できる数、 m:課題数、n:生徒数、 p:袋数
    int d, k, l, m, n, p;
    while (cin >> d >> k >> l, d) {
        vii<int> c(d, vi<int>(k)); //部品の価格
        rep(i, d) rep(j, k) cin >> c[i][j];

        cin >> m >> n >> p;
        vii<int> r(m, vi<int>(k));
        vii<int> t(p, vi<int>(k));
        rep(i, m) rep(j, k) cin >> r[i][j]; //課題の部品数
        rep(i, p) rep(j, k) cin >> t[i][j]; //袋の部品数
        t.push_back(vi<int>());
        rep(j, k) t[p].push_back(0); //空の袋を入れる

        //dp[x][y][z]: x回目の授業, y:3^k通りで表す部品の状況, z個購入
        viii<ll> dp(d + 1, vii<ll>(pow3[k], vi<ll>(l + 1, inf)));
        dp[0][0][0] = 0;
        rep(x, d) rep(y, pow3[k]){
            if(x) rep(z, l + 1) chmin(dp[x][y][0], dp[x - 1][y][z]);
            rep(z, l) rep(w, k) {
                if ((y / pow3[w]) % 3 == 2) continue;
                chmin(dp[x][y + pow3[w]][z + 1],
                    dp[x][y][z] + c[x][w]);
            }
        }

        rep(y, pow3[k]) rep(z, l + 1) chmin(dp[d][y][0], dp[d - 1][y][z]);

        MCF mcf(m + p + 3);
        int S = m + p + 1, T = m + p + 2;

        rep(i, m) mcf.addedge(S, i, 1, 0);
        rep(i, p) mcf.addedge(i + m, T, 1, 0);
        mcf.addedge(m + p, T, n, 0);

        rep(i, m) rep(j, p + 1) { //i番目の課題, j番目の袋
            int bit = 0;
            bool ok = true;
            rep(h, k) { //h番目の部品
                if (r[i][h] < t[j][h]) { //袋の部品数のほうが多いとき
                    ok = false;
                    break;
                }
                bit += (r[i][h] - t[j][h]) * pow3[h];
            }
            if (!ok) continue;
            if (dp[d][bit][0] != inf) mcf.addedge(i, j + m, 1, dp[d][bit][0]);
        }

        ll cost = mcf.mincostflow(S, T, n);
        ans.push_back(cost);
    }
    for (ll elem : ans) cout << elem << endl;
    return 0;
}
accuracy: 1
time_ms: 490
memory_kb: 11616
final_rank: 10

code_snippet: #line 1 "a.cpp"
#include<iostream>
#include<algorithm>
using namespace std;
#line 1 "/home/kotatsugame/library/graph/MCF.cpp"
//Minimum Cost Flow O(FE log V)
#line 3 "/home/kotatsugame/library/graph/MCF.cpp"
#include<utility>
#include<vector>
#include<queue>
#include<limits>
template<typename T>
struct MCF{
	struct edge{
		int to,rev,cap;
		T cost;
	};
	vector<vector<edge> >G;
	vector<T>h,d;
	vector<int>pv,pe;
	MCF(int n_=0):G(n_),h(n_,0),d(n_),pv(n_),pe(n_){}
	void add_edge(int from,int to,int cap,T cost)
	{
		G[from].push_back({to,(int)G[to].size(),cap,cost});
		G[to].push_back({from,(int)G[from].size()-1,0,-cost});
	}
	T min_cost_flow(int s,int t,int f)//ans or -1
	{
		T ret=0;
		while(f>0)
		{
			priority_queue<pair<T,int>,vector<pair<T,int> >,greater<pair<T,int> > >P;
			fill(d.begin(),d.end(),numeric_limits<T>::max());
			d[s]=0;
			P.push(make_pair(0,s));
			while(!P.empty())
			{
				pair<T,int>p=P.top();P.pop();
				if(d[p.second]<p.first)continue;
				for(int i=0;i<G[p.second].size();i++)
				{
					edge&e=G[p.second][i];
					if(e.cap>0&&d[e.to]>d[p.second]+e.cost+h[p.second]-h[e.to])
					{
						d[e.to]=d[p.second]+e.cost+h[p.second]-h[e.to];
						pv[e.to]=p.second;
						pe[e.to]=i;
						P.push(make_pair(d[e.to],e.to));
					}
				}
			}
			if(d[t]==numeric_limits<T>::max())return -1;
			for(int u=0;u<G.size();u++)h[u]+=d[u];
			int d=f;
			for(int u=t;u!=s;u=pv[u])d=min(d,G[pv[u]][pe[u]].cap);
			f-=d;
			ret+=d*h[t];
			for(int u=t;u!=s;u=pv[u])
			{
				G[pv[u]][pe[u]].cap-=d;
				G[u][G[pv[u]][pe[u]].rev].cap+=d;
			}
		}
		return ret;
	}
};
#line 5 "a.cpp"
int D,K,L;
const int p3[9]={1,3,9,27,81,243,729,2187,6561};
int cost[6561],nxtcost[6561];
int c[8],t[8];
int M,N,P;
int R[200][8],T[200][8];
void dfs(int id,int now,int add,int sum)
{
	if(id==0)
	{
		if(cost[now]<(int)1e9)nxtcost[now+add]=min(nxtcost[now+add],cost[now]+sum);
		return;
	}
	for(int i=0;i<=2-t[id-1];i++)
	{
		dfs(id-1,now*3+i,add,sum);
	}
}
main()
{
	while(cin>>D>>K>>L,D)
	{
		cost[0]=0;
		for(int i=1;i<p3[K];i++)cost[i]=1e9;
		for(int day=0;day<D;day++)
		{
			for(int i=0;i<K;i++)cin>>c[i];
			for(int i=0;i<p3[K];i++)nxtcost[i]=1e9;
			for(int i=0;i<p3[K];i++)
			{
				int u=i,sum=0,cnt=0;
				for(int j=0;j<K;j++)
				{
					t[j]=u%3;
					cnt+=t[j];
					sum+=t[j]*c[j];
					u/=3;
				}
				if(cnt>L)continue;
				dfs(K,0,i,sum);
			}
			for(int i=0;i<p3[K];i++)cost[i]=nxtcost[i];
		}
		cin>>M>>N>>P;
		for(int i=0;i<M;i++)for(int j=0;j<K;j++)cin>>R[i][j];
		for(int i=0;i<P;i++)for(int j=0;j<K;j++)cin>>T[i][j];
		MCF<int>Q(M+P+2);
		int st=M+P,go=st+1;
		for(int i=0;i<M;i++)
		{
			Q.add_edge(st,i,1,0);
			int now=0;
			for(int j=K;j--;)now=now*3+R[i][j];
			if(cost[now]<(int)1e9)Q.add_edge(i,go,1,cost[now]);
		}
		for(int i=0;i<P;i++)Q.add_edge(M+i,go,1,0);
		for(int i=0;i<M;i++)for(int j=0;j<P;j++)
		{
			int now=0;
			bool neg=false;
			for(int k=K;k--;)
			{
				int t=R[i][k]-T[j][k];
				if(t<0){neg=true;break;}
				now=now*3+t;
			}
			if(neg)continue;
			if(cost[now]<(int)1e9)Q.add_edge(i,M+j,1,cost[now]);
		}
		cout<<Q.min_cost_flow(st,go,N)<<endl;
	}
}
accuracy: 1
time_ms: 1840
memory_kb: 4684
final_rank: 12

code_snippet: #include <vector>
#include <cassert>
#include <iostream>
#include <algorithm>
using namespace std;
const int inf = 1012345678;
int gethash(int K, vector<int> v) {
	assert(int(v.size()) == K);
	int res = 0;
	for (int i = 0; i < K; ++i) {
		res = res * 3 + v[i];
	}
	return res;
}
vector<int> getrevhash(int K, int val) {
	vector<int> ans(K);
	for (int i = K - 1; i >= 0; --i) {
		ans[i] = val % 3;
		val /= 3;
	}
	return ans;
}
int main() {
	while (true) {
		int D, K, L, M, N, P;
		cin >> D >> K >> L;
		if (D == 0 && K == 0 && L == 0) {
			break;
		}
		vector<vector<int> > cost(D, vector<int>(K));
		for (int i = 0; i < D; ++i) {
			for (int j = 0; j < K; ++j) {
				cin >> cost[i][j];
			}
		}
		cin >> M >> N >> P;
		vector<vector<int> > req(M, vector<int>(K));
		for (int i = 0; i < M; ++i) {
			for (int j = 0; j < K; ++j) {
				cin >> req[i][j];
			}
		}
		vector<vector<int> > box(P + N, vector<int>(K));
		for (int i = 0; i < P; ++i) {
			for (int j = 0; j < K; ++j) {
				cin >> box[i][j];
			}
		}
		P += N;
		int mul = 1;
		for (int i = 0; i < K; ++i) {
			mul *= 3;
		}
		vector<int> dp(mul, inf);
		dp[0] = 0;
		for (int i = 0; i < D; ++i) {
			for (int j = 0; j < L; ++j) {
				vector<int> ndp = dp;
				for (int k = mul - 1; k >= 0; --k) {
					vector<int> v = getrevhash(K, k);
					for (int l = 0; l < K; ++l) {
						if (v[l] == 0) continue;
						--v[l];
						ndp[k] = min(ndp[k], dp[gethash(K, v)] + cost[i][l]);
						++v[l];
					}
				}
				dp = ndp;
			}
		}
		vector<vector<int> > G(M, vector<int>(P, inf));
		for (int i = 0; i < M; ++i) {
			for (int j = 0; j < P; ++j) {
				bool ok = true;
				vector<int> need(K);
				for (int k = 0; k < K; ++k) {
					if (req[i][k] < box[j][k]) {
						ok = false;
					}
					need[k] = max(req[i][k] - box[j][k], 0);
				}
				if (ok) {
					G[i][j] = dp[gethash(K, need)];
				}
			}
		}
		int ans = 0;
		vector<int> match(M + P, -1);
		for (int i = 0; i < N; ++i) {
			vector<int> dist(M + P, inf), par(M + P, -1);
			for (int j = 0; j < M; ++j) {
				if (match[j] == -1) {
					dist[j] = 0;
				}
			}
			while (true) {
				vector<int> ndist = dist;
				for (int j = 0; j < M; ++j) {
					if (match[j] != -1) {
						int nd = ndist[match[j]] - G[j][match[j] - M];
						if (ndist[j] > nd) {
							ndist[j] = nd;
							par[j] = match[j];
						}
					}
				}
				for (int j = M; j < M + P; ++j) {
					for (int k = 0; k < M; ++k) {
						if (match[j] != k) {
							int nd = ndist[k] + G[k][j - M];
							if (ndist[j] > nd) {
								ndist[j] = nd;
								par[j] = k;
							}
						}
					}
				}
				if (dist == ndist) break;
				dist = ndist;
			}
			int opt = inf, mn = -1;
			for (int j = M; j < M + P; ++j) {
				if (match[j] == -1 && dist[j] < opt) {
					opt = dist[j];
					mn = j;
				}
			}
			if (mn == -1) {
				ans = inf;
				break;
			}
			ans += dist[mn];
			while (par[mn] != -1) {
				if (mn >= M) {
					match[mn] = par[mn];
					match[par[mn]] = mn;
				}
				mn = par[mn];
			}
		}
		cout << (ans == inf ? -1 : ans) << endl;
	}
	return 0;
}
accuracy: 1
time_ms: 3580
memory_kb: 3376
final_rank: 13

code_snippet: #include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <numeric>
#include <bitset>
#include <cmath>

static const int MOD = 1000000007;
using ll = long long;
using u32 = unsigned;
using u64 = unsigned long long;
using namespace std;

template<class T> constexpr T INF = ::numeric_limits<T>::max()/32*15+208;

template <class T, class U>
vector<T> make_v(U size, const T& init){ return vector<T>(static_cast<size_t>(size), init); }

template<class... Ts, class U>
auto make_v(U size, Ts... rest) { return vector<decltype(make_v(rest...))>(static_cast<size_t>(size), make_v(rest...)); }

template<class T> void chmin(T &a, const T &b){ a = (a < b ? a : b); }
template<class T> void chmax(T &a, const T &b){ a = (a > b ? a : b); }

template<typename F, typename C>
struct PrimalDual {
    struct edge {
        int to; F cap; C cost; int rev;
        edge() = default;
        edge(int to, F cap, C cost, int rev):to(to), cap(cap), cost(cost), rev(rev) {};
    };
    vector<vector<edge>> G;
    vector<C> potential, min_cost;
    vector<int> prevv, preve;

    explicit PrimalDual(int n) : G(n), potential(n), min_cost(n), prevv(n), preve(n) {}

    void add_edge(int u, int v, F cap, C cost){
        G[u].emplace_back(v, cap, cost, G[v].size());
        G[v].emplace_back(u, 0, -cost, G[u].size()-1);
    }

    struct P{
        C first; int second;
        P(C first,int second):first(first),second(second){}
        bool operator<(const P&a) const{return a.first<first;}
    };
    void dijkstra(int s){
        priority_queue<P> Q;
        fill(min_cost.begin(),min_cost.end(), INF<C>);
        min_cost[s] = 0;
        Q.emplace(0, s);
        while(!Q.empty()){
            P p = Q.top(); Q.pop();
            int v = p.second;
            if(min_cost[v] < p.first) continue;
            for(int i = 0; i < G[v].size(); ++i){
                edge &e=G[v][i];
                if(e.cap==0) continue;
                if(min_cost[v]+e.cost+potential[v]-potential[e.to] < min_cost[e.to]){
                    min_cost[e.to] = min_cost[v]+e.cost+potential[v]-potential[e.to];
                    prevv[e.to] = v;
                    preve[e.to] = i;
                    Q.emplace(min_cost[e.to], e.to);
                }
            }
        }
    }

    C flow(int s, int t, F fl, int &ok){
        C res = 0;
        fill(potential.begin(),potential.end(), 0);
        while(fl > 0){
            dijkstra(s);
            if(min_cost[t] == INF<C>){
                ok = 0;
                return res;
            }
            for (int i = 0; i < potential.size(); ++i) {
                if(min_cost[i] < INF<C>) potential[i] += min_cost[i];
            }

            F d = fl;
            for(int v = t; v != s; v = prevv[v]){
                d = min(d, G[prevv[v]][preve[v]].cap);
            }
            fl -= d;
            res += potential[t]*d;
            for(int v = t; v != s; v = prevv[v]){
                G[prevv[v]][preve[v]].cap -= d;
                G[v][G[prevv[v]][preve[v]].rev].cap += d;
            }
        }
        ok = 1;
        return res;
    }
};

void solve(int d, int k, int l){
    vector<vector<int>> v(d, vector<int>(k, 0));
    for (int i = 0; i < d; ++i) {
        for (int j = 0; j < k; ++j) {
            cin >> v[i][j];
        }
    }
    auto dp = make_v(d+1, l+1, 1 << (2*k), INF<int>);
    dp[0][0][0] = 0;
    for (int i = 0; i < d; ++i) {
        for (int j = 0; j <= l; ++j) {
            for (int m = 0; m < 1 << (2*k); ++m) {
                chmin(dp[i+1][0][m], dp[i][j][m]);
                if(j == l || dp[i][j][m] == INF<int>) continue;
                for (int n = 0; n < k; ++n) {
                    if(((m >> 2*n)&3) == 2) continue;
                    chmin(dp[i][j+1][m+(1 << 2*n)], dp[i][j][m]+v[i][n]);
                }
            }
        }
    }
    int m, n, p;
    cin >> m >> n >> p;
    auto R = make_v(m, k, 0);
    auto T = make_v(p+1, k, 0);
    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < k; ++j) {
            cin >> R[i][j];
        }
    }
    for (int i = 0; i < p; ++i) {
        for (int j = 0; j < k; ++j) {
            cin >> T[i][j];
        }
    }
    p++;
    PrimalDual<int, int> G(m+p+2);
    for (int i = 0; i < m; ++i) {
        G.add_edge(0, i+2, 1, 0);
    }
    for (int i = 0; i < p; ++i) {
        G.add_edge(m+2+i, 1, (i == p-1 ? n : 1), 0);
    }
    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < p; ++j) {
            int S = 0;
            for (int o = 0; o < k; ++o) {
                if(R[i][o] < T[j][o]) {
                    S = -1;
                    break;
                }
                S |= (1 << 2*o)*(R[i][o]-T[j][o]);
            }
            if(S >= 0 && dp.back()[0][S] < INF<int>){
                G.add_edge(i+2, m+2+j, 1, dp.back()[0][S]);
            }
        }
    }
    int ok = 0;
    auto res = G.flow(0, 1, n, ok);
    if(!ok) puts("-1");
    else cout << res << "\n";
}

int main() {
    int d, k, l;
    while(cin >> d >> k >> l, d){
        solve(d, k, l);
    }
    return 0;
}
accuracy: 1
time_ms: 500
memory_kb: 26116
final_rank: 14

code_snippet: #include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <numeric>
#include <bitset>
#include <cmath>

static const int MOD = 1000000007;
using ll = long long;
using u32 = unsigned;
using u64 = unsigned long long;
using namespace std;

template<class T> constexpr T INF = ::numeric_limits<T>::max()/32*15+208;

template <class T, class U>
vector<T> make_v(U size, const T& init){ return vector<T>(static_cast<size_t>(size), init); }

template<class... Ts, class U>
auto make_v(U size, Ts... rest) { return vector<decltype(make_v(rest...))>(static_cast<size_t>(size), make_v(rest...)); }

template<class T> void chmin(T &a, const T &b){ a = (a < b ? a : b); }
template<class T> void chmax(T &a, const T &b){ a = (a > b ? a : b); }

template<typename F, typename C>
struct PrimalDual {
    struct edge {
        int to; F cap; C cost; int rev;
        edge() = default;
        edge(int to, F cap, C cost, int rev):to(to), cap(cap), cost(cost), rev(rev) {};
    };
    vector<vector<edge>> G;
    vector<C> potential, min_cost;
    vector<int> prevv, preve;

    explicit PrimalDual(int n) : G(n), potential(n), min_cost(n), prevv(n), preve(n) {}

    void add_edge(int u, int v, F cap, C cost){
        G[u].emplace_back(v, cap, cost, G[v].size());
        G[v].emplace_back(u, 0, -cost, G[u].size()-1);
    }

    struct P{
        C first; int second;
        P(C first,int second):first(first),second(second){}
        bool operator<(const P&a) const{return a.first<first;}
    };
    void dijkstra(int s){
        priority_queue<P> Q;
        fill(min_cost.begin(),min_cost.end(), INF<C>);
        min_cost[s] = 0;
        Q.emplace(0, s);
        while(!Q.empty()){
            P p = Q.top(); Q.pop();
            int v = p.second;
            if(min_cost[v] < p.first) continue;
            for(int i = 0; i < G[v].size(); ++i){
                edge &e=G[v][i];
                if(e.cap==0) continue;
                if(min_cost[v]+e.cost+potential[v]-potential[e.to] < min_cost[e.to]){
                    min_cost[e.to] = min_cost[v]+e.cost+potential[v]-potential[e.to];
                    prevv[e.to] = v;
                    preve[e.to] = i;
                    Q.emplace(min_cost[e.to], e.to);
                }
            }
        }
    }

    C flow(int s, int t, F fl, int &ok){
        C res = 0;
        fill(potential.begin(),potential.end(), 0);
        while(fl > 0){
            dijkstra(s);
            if(min_cost[t] == INF<C>){
                ok = 0;
                return res;
            }
            for (int i = 0; i < potential.size(); ++i) {
                if(min_cost[i] < INF<C>) potential[i] += min_cost[i];
            }

            F d = fl;
            for(int v = t; v != s; v = prevv[v]){
                d = min(d, G[prevv[v]][preve[v]].cap);
            }
            fl -= d;
            res += potential[t]*d;
            for(int v = t; v != s; v = prevv[v]){
                G[prevv[v]][preve[v]].cap -= d;
                G[v][G[prevv[v]][preve[v]].rev].cap += d;
            }
        }
        ok = 1;
        return res;
    }
};

void solve(int d, int k, int l){
    vector<vector<int>> v(d, vector<int>(k, 0));
    for (int i = 0; i < d; ++i) {
        for (int j = 0; j < k; ++j) {
            cin >> v[i][j];
        }
    }
    auto dp = make_v(d+1, l+1, 1 << (2*k), INF<int>);
    dp[0][0][0] = 0;
    for (int i = 0; i < d; ++i) {
        for (int j = 0; j <= l; ++j) {
            for (int m = 0; m < 1 << (2*k); ++m) {
                chmin(dp[i+1][0][m], dp[i][j][m]);
                if(j == l || dp[i][j][m] == INF<int>) continue;
                for (int n = 0; n < k; ++n) {
                    if(((m >> 2*n)&3) == 2) continue;
                    chmin(dp[i][j+1][m+(1 << 2*n)], dp[i][j][m]+v[i][j]);
                }
            }
        }
    }
    int m, n, p;
    cin >> m >> n >> p;
    auto R = make_v(m, k, 0);
    auto T = make_v(p+1, k, 0);
    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < k; ++j) {
            cin >> R[i][j];
        }
    }
    for (int i = 0; i < p; ++i) {
        for (int j = 0; j < k; ++j) {
            cin >> T[i][j];
        }
    }
    p++;
    PrimalDual<int, int> G(m+p+2);
    for (int i = 0; i < m; ++i) {
        G.add_edge(0, i+2, 1, 0);
    }
    for (int i = 0; i < p; ++i) {
        G.add_edge(m+2+i, 1, 1, 0);
    }
    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < p; ++j) {
            int S = 0;
            for (int o = 0; o < k; ++o) {
                if(R[i][o] < T[j][o]) {
                    S = -1;
                    break;
                }
                S |= (1 << 2*o)*(R[i][o]-T[j][o]);
            }
            if(S >= 0 && dp.back()[0][S] < INF<int>){
                G.add_edge(i+2, m+2+j, 1, dp.back()[0][S]);
            }
        }
    }
    int ok = 0;
    auto res = G.flow(0, 1, n, ok);
    if(!ok) puts("-1");
    else cout << res << "\n";
}

int main() {
    int d, k, l;
    while(cin >> d >> k >> l, d){
        solve(d, k, l);
    }
    return 0;
}
accuracy: 0.125
time_ms: 10
memory_kb: 18284
final_rank: 18

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

using ll = long long int;
using P = pair<int, int>;
using P3 = pair<int, P>;
using PP = pair<P, P>;
constexpr int INF = 1 << 30;
constexpr ll MOD = ll(1e9) + 7;
constexpr int di[] = {0, 1, 0, -1};
constexpr int dj[] = {1, 0, -1, 0};

void chmin(int &a, int b){
    a = min(a, b);
}

struct edge{
    int to, cap, cost, rev;
};

vector<vector<edge> > G;
vector<int> h, dist, prevv, preve;

void add_edge(int from, int to, int cap, int cost){
    G[from].push_back((edge){to, cap, cost, int(G[to].size())});
    G[to].push_back((edge){from, 0, -cost, int(G[from].size())-1});
}

// 最小費用流
int min_cost_flow(int s, int t, int f){
    h.resize(G.size());
    dist.resize(G.size());
    prevv.resize(G.size());
    preve.resize(G.size());
    int res = 0;
    fill(h.begin(), h.end(), 0);
    while(f > 0){
        priority_queue<P, vector<P>, greater<P> > que;
        fill(dist.begin(), dist.end(), INF);
        dist[s] = 0;
        que.push({0,s});
        while(!que.empty()){
            P p = que.top();
            que.pop();
            int v = p.second;
            if(dist[v] < p.first) continue;
            for(int i=0;i<int(G[v].size());i++){
                auto &e = G[v][i];
                if(e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]){
                    dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
                    prevv[e.to] = v;
                    preve[e.to] = i;
                    que.push({dist[e.to], e.to});
                }
            }
        }
        if(dist[t] == INF){
            return -1;
        }
        for(int v=0; v<G.size(); v++) h[v] += dist[v];
        int d = f;
        for(int v=t; v!=s; v=prevv[v]){
            d = min(d, G[prevv[v]][preve[v]].cap);
        }
        f -= d;
        res += d*h[t];
        for(int v=t; v!=s; v=prevv[v]){
            auto &e = G[prevv[v]][preve[v]];
            e.cap -= d;
            G[v][e.rev].cap += d;
        }
    }
    return res;
}

int dp[9][9][1<<16];

bool solve(){
    int D, K, L, N, M, Q;
    cin >> D >> K >> L;
    if(D == 0) return true;
    vector<vector<int> > c(D, vector<int>(K));
    for(int i=0;i<D;i++){
        for(int j=0;j<K;j++){
            cin >> c[i][j];
        }
    }
    cin >> M >> N >> Q;
    vector<vector<int> > r(M, vector<int>(K)), t(Q+1, vector<int>(K));
    for(int i=0;i<M;i++){
        for(int j=0;j<K;j++){
            cin >> r[i][j];
        }
    }
    for(int i=0;i<Q;i++){
        for(int j=0;j<K;j++){
            cin >> t[i][j];
        }
    }
    for(int i=0;i<K;i++){
        t[Q][i] = 0;
    }
    Q++;
    for(int i=0;i<=D;i++){
        for(int j=0;j<=L;j++){
            fill(dp[i][j], dp[i][j]+(1<<(K*2)), INF);
        }
    }
    dp[0][0][0] = 0;
    for(int i=0;i<D;i++){
        for(int j=0;j<=L;j++){
            for(int X=0;X<(1<<(K*2));X++){
                if(dp[i][j][X] == INF) continue;
                chmin(dp[i+1][0][X], dp[i][j][X]);
                if(j==L) continue;
                int Y = X;
                for(int k=0;k<K;k++,Y>>2){
                    if((Y&3)>=2) continue;
                    chmin(dp[i][j+1][X+(1<<(k*2))], dp[i][j][X]+c[i][k]);
                }
            }
        }
    }
    G.clear();
    G.resize(M+Q+2);
    int S = M+Q, T = M+Q+1;
    for(int i=0;i<M;i++){
        for(int j=0;j<Q;j++){
            int X = 0;
            bool ok = true;
            for(int k=0;k<K;k++){
                if(r[i][k] < t[j][k]){
                    ok = false;
                    break;
                }
                X |= (r[i][k]-t[j][k])<<(k*2);
            }
            if(ok && dp[D][0][X] < INF){
                assert(X < 1<<(K*2));
                add_edge(i,M+j,1,dp[D][0][X]);
            }
        }
    }
    for(int i=0;i<M;i++){
        add_edge(S,i,1,0);
    }
    for(int i=0;i<Q;i++){
        add_edge(M+i,T,(i==Q-1?INF:1),0);
    }
    int ans = min_cost_flow(S,T,N);
    
    cout << ans << endl;
    return false;
}

int main() {
    while(!solve());
    return 0;
}
accuracy: 0.125
time_ms: 40
memory_kb: 20344
final_rank: 19

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

using ll = long long int;
using P = pair<int, int>;
using P3 = pair<int, P>;
using PP = pair<P, P>;
constexpr int INF = 1 << 29;
constexpr ll MOD = ll(1e9) + 7;
constexpr int di[] = {0, 1, 0, -1};
constexpr int dj[] = {1, 0, -1, 0};

void chmin(int &a, int b){
    a = min(a, b);
}

struct edge{
    int to, cap, cost, rev;
};

vector<vector<edge> > G;
vector<int> h, dist, prevv, preve;

void add_edge(int from, int to, int cap, int cost){
    G[from].push_back((edge){to, cap, cost, int(G[to].size())});
    G[to].push_back((edge){from, 0, -cost, int(G[from].size())-1});
}

// 最小費用流
int min_cost_flow(int s, int t, int f){
    h.resize(G.size());
    dist.resize(G.size());
    prevv.resize(G.size());
    preve.resize(G.size());
    int res = 0;
    fill(h.begin(), h.end(), 0);
    while(f > 0){
        priority_queue<P, vector<P>, greater<P> > que;
        fill(dist.begin(), dist.end(), INF);
        dist[s] = 0;
        que.push({0,s});
        while(!que.empty()){
            P p = que.top();
            que.pop();
            int v = p.second;
            if(dist[v] < p.first) continue;
            for(int i=0;i<int(G[v].size());i++){
                auto &e = G[v][i];
                if(e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]){
                    dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
                    prevv[e.to] = v;
                    preve[e.to] = i;
                    que.push({dist[e.to], e.to});
                }
            }
        }
        if(dist[t] == INF){
            return -1;
        }
        for(int v=0; v<G.size(); v++) h[v] += dist[v];
        int d = f;
        for(int v=t; v!=s; v=prevv[v]){
            d = min(d, G[prevv[v]][preve[v]].cap);
        }
        f -= d;
        res += d*h[t];
        for(int v=t; v!=s; v=prevv[v]){
            auto &e = G[prevv[v]][preve[v]];
            e.cap -= d;
            G[v][e.rev].cap += d;
        }
    }
    return res;
}

int dp[9][9][1<<17];

bool solve(){
    int D, K, L, N, M, Q;
    cin >> D >> K >> L;
    if(D == 0) return true;
    vector<vector<int> > c(D, vector<int>(K));
    for(int i=0;i<D;i++){
        for(int j=0;j<K;j++){
            cin >> c[i][j];
        }
    }
    cin >> M >> N >> Q;
    vector<vector<int> > r(M, vector<int>(K)), t(Q+1, vector<int>(K));
    for(int i=0;i<M;i++){
        for(int j=0;j<K;j++){
            cin >> r[i][j];
        }
    }
    for(int i=0;i<Q;i++){
        for(int j=0;j<K;j++){
            cin >> t[i][j];
        }
    }
    for(int i=0;i<K;i++){
        t[Q][i] = 0;
    }
    Q++;
    for(int i=0;i<=D;i++){
        for(int j=0;j<=L;j++){
            fill(dp[i][j], dp[i][j]+(1<<(K*2)), INF);
        }
    }
    dp[0][0][0] = 0;
    for(int i=0;i<D;i++){
        for(int j=0;j<=L;j++){
            for(int X=0;X<(1<<(K*2));X++){
                if(dp[i][j][X] == INF) continue;
                chmin(dp[i+1][0][X], dp[i][j][X]);
                if(j==L) continue;
                int Y = X;
                for(int k=0;k<K;k++,Y>>2){
                    if((Y&3)>=2) continue;
                    chmin(dp[i][j+1][X+(1<<(k*2))], dp[i][j][X]+c[i][k]);
                }
            }
        }
    }
    G.clear();
    G.resize(M+Q+2);
    int S = M+Q, T = M+Q+1;
    for(int i=0;i<M;i++){
        for(int j=0;j<Q;j++){
            int X = 0;
            bool ok = true;
            for(int k=0;k<K;k++){
                if(r[i][k] < t[j][k]){
                    ok = false;
                    break;
                }
                X |= (r[i][k]-t[j][k])<<(k*2);
            }
            if(ok && dp[D][0][X] < INF){
                add_edge(i,M+j,1,dp[D][0][X]);
            }
        }
    }
    for(int i=0;i<M;i++){
        add_edge(S,i,1,0);
    }
    for(int i=0;i<Q;i++){
        add_edge(M+i,T,(i==Q-1?INF:1),0);
    }
    int ans = min_cost_flow(S,T,N);
    
    cout << ans << endl;
    return false;
}

int main() {
    while(!solve());
    return 0;
}
accuracy: 0.125
time_ms: 40
memory_kb: 23108
final_rank: 20

code_snippet: #include <cstdio>
#include <queue>
#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
const int INF = 1 << 29;
int shift[10];

struct Edge {
    int to, cap, cost, rev;
    Edge(int t, int c, int r) : to(t), cap(c), rev(r) {}
    Edge(int t, int ca, int co, int r) : to(t), cap(ca), cost(co), rev(r) {}
};

using Graph = vector< vector< Edge > >;
struct Flow {
    vector< vector<Edge> > G;
    const int MAXC = 1 << 29;
    int n;
    vector<bool> used;
    vector<int> prevv, preve, dist;
    Flow(int n_) : G(n_), n(n_), used(n_, false), prevv(n_), preve(n_), dist(n_, MAXC) {}

    void add_edge(int from, int to, int cap, int cost) {
        G[from].push_back(Edge(to, cap, cost, G[to].size()));
        G[to  ].push_back(Edge(from, 0, -cost, G[from].size() - 1));
    }

    int dfs(int v, int t, int f) {
        if(v == t) return f;
        used[v] = true;
    }


    // ベルマンフォード法で最小費用流を解く
    // 負辺や負閉路があるときは基本的にこちらのほうが確実
    int mincost_flow(int s, int t, int f) {
        int res = 0;
        int ma = MAXC;
        while(f > 0) {
            fill(dist.begin(), dist.end(), ma);
            dist[s] = 0;
            bool update = true;
            while(update) {
                update = false;
                for(int v = 0; v < n; v++) {
                    if(dist[v] == ma) continue;
                    for(int i=0; i<G[v].size(); i++) {
                        Edge &e = G[v][i];
                        if(e.cap>0 && dist[e.to] > dist[v] + e.cost) {
                            dist[e.to] = dist[v] + e.cost;
                            prevv[e.to] = v;
                            preve[e.to] = i;
                            update = true;
                        }
                    }
                }
            }

            if(dist[t] == ma) return -1;
            int d = f;
            for(int v = t; v != s; v = prevv[v]) {
                d = min(d, G[prevv[v]][preve[v]].cap);
            }
            f -= d;
            res += d * dist[t];
            for(int v = t; v != s; v = prevv[v]) {
                Edge &e = G[prevv[v]][preve[v]];
                e.cap -= d;
                G[v][e.rev].cap += d;
            }
        }
        return res;
    }    
    
    int fast_mincost_flow(int s, int t, int f) {
        int res = 0, ma = MAXC;
        vector<int> pot(n);
        while(f > 0) {
            using PT = pair<int, int>;
            priority_queue< PT, vector<PT>, greater<PT> > que;
            fill(dist.begin(), dist.end(), ma);

            dist[s] = 0;
            que.push(make_pair(0, s));
            while(que.size()) {
                PT cur = que.top(); que.pop();
                int v = cur.second;
                if(dist[v] < cur.first) continue;
                for(size_t i=0; i<G[v].size(); i++) {
                    Edge &e = G[v][i];
                    if(e.cap > 0 and dist[e.to] > dist[v] + e.cost + pot[v] - pot[e.to]) {
                        dist[e.to] = dist[v] + e.cost + pot[v] - pot[e.to];
                        prevv[e.to] = v;
                        preve[e.to] = i;
                        que.push(make_pair(dist[e.to], e.to));
                    }
                }
            }
            if(dist[t] == ma) {
                return -1;
            }
            for(int v=0; v<n; v++) pot[v] += dist[v];

            int d = f;
            for(int v=t; v!=s; v=prevv[v]) {
                d = min(d, G[prevv[v]][preve[v]].cap);
            }
            f -= d;
            res += d * pot[t];
            for(int v=t; v!=s; v=prevv[v]) {
                Edge &e = G[prevv[v]][preve[v]];
                e.cap -= d;
                G[v][e.rev].cap += d;
            }
        }
        return res;
    }
};

void chmin(int &A, int B) {
    A = min(A, B);
}

int D, K, L, M, N, P;
int c[10][10], r[210][10], t[210][10];

int encode(vector<int> cnt) {
    int sz = cnt.size(), res = 0;
    for(int i=sz-1; i>=0; i--) {
        res = res * 3 + cnt[i];
    }
    return res;
}

int get_digit(int val, int k) {
    val /= shift[k];
    return val % 3;
}

int update_digit(int bit, int k, int val) {
    bit -= get_digit(bit, k) * shift[k];
    bit += val * shift[k];
    return bit;
}

vector<int> solve_dp(int day, const vector<int> &vec) {
    int lim = shift[K];
    vector< vector< vector<int> > > dp(K+1, vector< vector<int> >(L+1, vector<int>(lim, INF)));
    dp[0][0] = vec;

    for(int i=0; i<K; i++) {
        for(int j=0; j<=L; j++) {
            for(int bit=0; bit<lim; bit++) {
                int v = get_digit(bit, i);
                for(int x=0; x+v<=2 and x+j<=L; x++) {
                    int nbit = update_digit(bit, i, x+v);
                    int cost = c[day][i] * x;
                    chmin(dp[i+1][j+x][nbit], dp[i][j][bit] + cost);
                }
            }
        }
    }

    vector<int> res = vec;
    for(int bit=0; bit<lim; bit++) {
        for(int i=0; i<=L; i++) res[bit] = min(res[bit], dp[K][i][bit]);
    }
    return res;
}

int solve_testcase() {
    cin >> D >> K >> L;
    if(D == 0 and K == 0 and L == 0) return 1;
    for(int i=0; i<D; i++) {
        for(int j=0; j<K; j++) {
            cin >> c[i][j];
        }
    }

    // solve (DP);
    vector<int> vec(shift[K], INF);
    vec[0] = 0;
    for(int i=0; i<D; i++) {
        vec = solve_dp(i, vec);
    }

    /*
      for(int i=0; i<shift[K]; i++) {
      fprintf(stderr, "vec[%d] = %d\n", i, vec[i]);
      }
    */

    cin >> M >> N >> P;
    for(int i=0; i<M; i++) {
        for(int j=0; j<K; j++) {
            cin >> r[i][j];
        }
    }

    for(int i=0; i<P; i++) {
        for(int j=0; j<K; j++) {
            cin >> t[i][j];
        }
    }
  
    Flow fl(M + P + 2);
    int source = M + P, sink = source + 1;
    for(int i=0; i<P; i++) fl.add_edge(source, i, 1, 0);
    for(int i=0; i<M; i++) fl.add_edge(P+i, sink, 1, 0);

    // fprintf(stderr, "source sink end\n");
    vector<int> cnt(K);
    for(int i=0; i<P; i++) {
        for(int j=0; j<M; j++) {
            fill(cnt.begin(), cnt.end(), 0);

            // fprintf(stderr, "cnt = %zu\n", cnt.size());
            // fprintf(stderr, "fill end\n");
            for(int k=0; k<K; k++) cnt[k] += r[j][k];
            for(int k=0; k<K; k++) cnt[k] -= t[i][k];

            bool ok = true;
            for(int k=0; k<K; k++) ok &= (cnt[k] >= 0);
            if(!ok) continue;

            // fprintf(stderr, "before encode\n");
            int bit = encode(cnt);
            // fprintf(stderr, "bit = %d, size = %zu\n", bit, vec.size());
            if(vec[bit] == INF) continue;
            // fprintf(stderr, "add edge: [%d, %d, %d]\n", i, j, vec[bit]);
            fl.add_edge(i, P+j, 1, vec[bit]);
        }
    }

    for(int i=0; i<M; i++) {
        fill(cnt.begin(), cnt.end(), 0);
        for(int k=0; k<K; k++) cnt[k] += r[i][k];
        int bit = encode(cnt);
        if(vec[bit] == INF) continue;
        // fprintf(stderr, "# add edge: [source, %d, %d]\n", i, vec[bit]);
        fl.add_edge(source, P+i, 1, vec[bit]);
    }

    // fprintf(stderr, "before flow\n");
    int ans = fl.fast_mincost_flow(source, sink, N);
    cout << ans << endl;
    return 0;
}

int main() {
    shift[0] = 1;
    for(int i=1; i<10; i++) shift[i] = shift[i-1] * 3;
  
    while(!solve_testcase());
}
accuracy: 1
time_ms: 1340
memory_kb: 5256
final_rank: 8

code_snippet: #include <cstdio>
#include <queue>
#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
const int INF = 1 << 29;
int shift[10];

struct Edge {
    int to, cap, cost, rev;
    Edge(int t, int c, int r) : to(t), cap(c), rev(r) {}
    Edge(int t, int ca, int co, int r) : to(t), cap(ca), cost(co), rev(r) {}
};

using Graph = vector< vector< Edge > >;
struct Flow {
    vector< vector<Edge> > G;
    const int MAXC = 1 << 29;
    int n;
    vector<bool> used;
    vector<int> prevv, preve, dist;
    Flow(int n_) : G(n_), n(n_), used(n_, false), prevv(n_), preve(n_), dist(n_, MAXC) {}

    void add_edge(int from, int to, int cap, int cost) {
        G[from].push_back(Edge(to, cap, cost, G[to].size()));
        G[to  ].push_back(Edge(from, 0, -cost, G[from].size() - 1));
    }

    int dfs(int v, int t, int f) {
        if(v == t) return f;
        used[v] = true;
    }


    // ベルマンフォード法で最小費用流を解く
    // 負辺や負閉路があるときは基本的にこちらのほうが確実
    int mincost_flow(int s, int t, int f) {
        int res = 0;
        int ma = MAXC;
        while(f > 0) {
            fill(dist.begin(), dist.end(), ma);
            dist[s] = 0;
            bool update = true;
            while(update) {
                update = false;
                for(int v = 0; v < n; v++) {
                    if(dist[v] == ma) continue;
                    for(int i=0; i<G[v].size(); i++) {
                        Edge &e = G[v][i];
                        if(e.cap>0 && dist[e.to] > dist[v] + e.cost) {
                            dist[e.to] = dist[v] + e.cost;
                            prevv[e.to] = v;
                            preve[e.to] = i;
                            update = true;
                        }
                    }
                }
            }

            if(dist[t] == ma) return -1;
            int d = f;
            for(int v = t; v != s; v = prevv[v]) {
                d = min(d, G[prevv[v]][preve[v]].cap);
            }
            f -= d;
            res += d * dist[t];
            for(int v = t; v != s; v = prevv[v]) {
                Edge &e = G[prevv[v]][preve[v]];
                e.cap -= d;
                G[v][e.rev].cap += d;
            }
        }
        return res;
    }    
    
    int fast_mincost_flow(int s, int t, int f) {
        int res = 0, ma = MAXC;
        vector<int> pot(n);
        while(f > 0) {
            using PT = pair<int, int>;
            priority_queue< PT, vector<PT>, greater<PT> > que;
            fill(dist.begin(), dist.end(), ma);

            dist[s] = 0;
            que.push(make_pair(0, s));
            while(que.size()) {
                PT cur = que.top(); que.pop();
                int v = cur.second;
                if(dist[v] < cur.first) continue;
                for(size_t i=0; i<G[v].size(); i++) {
                    Edge &e = G[v][i];
                    if(e.cap > 0 and dist[e.to] > dist[v] + e.cost + pot[v] - pot[e.to]) {
                        dist[e.to] = dist[v] + e.cost + pot[v] - pot[e.to];
                        prevv[e.to] = v;
                        preve[e.to] = i;
                        que.push(make_pair(dist[e.to], e.to));
                    }
                }
            }
            if(dist[t] == ma) {
                return -1;
            }
            for(int v=0; v<n; v++) pot[v] += dist[v];

            int d = f;
            for(int v=t; v!=s; v=prevv[v]) {
                d = min(d, G[prevv[v]][preve[v]].cap);
            }
            f -= d;
            res += d * pot[t];
            for(int v=t; v!=s; v=prevv[v]) {
                Edge &e = G[prevv[v]][preve[v]];
                e.cap -= d;
                G[v][e.rev].cap += d;
            }
        }
        return res;
    }
};

void chmin(int &A, int B) {
    A = min(A, B);
}

int D, K, L, M, N, P;
int c[10][10], r[210][10], t[210][10];

int encode(vector<int> cnt) {
    int sz = cnt.size(), res = 0;
    for(int i=sz-1; i>=0; i--) {
        res = res * 3 + cnt[i];
    }
    return res;
}

int get_digit(int val, int k) {
    val /= shift[k];
    return val % 3;
}

int update_digit(int bit, int k, int val) {
    bit -= get_digit(bit, k) * shift[k];
    bit += val * shift[k];
    return bit;
}

vector<int> solve_dp(int day, const vector<int> &vec) {
    int lim = shift[K];
    vector< vector< vector<int> > > dp(K+1, vector< vector<int> >(L+1, vector<int>(lim, INF)));
    dp[0][0] = vec;

    for(int i=0; i<K; i++) {
        for(int j=0; j<=L; j++) {
            for(int bit=0; bit<lim; bit++) {
                int v = get_digit(bit, i);
                for(int x=0; x+v<=2 and x+j<=L; x++) {
                    int nbit = update_digit(bit, i, x+v);
                    int cost = c[day][i] * x;
                    chmin(dp[i+1][j+x][nbit], dp[i][j][bit] + cost);
                }
            }
        }
    }

    vector<int> res = vec;
    for(int bit=0; bit<lim; bit++) {
        for(int i=0; i<=L; i++) res[bit] = min(res[bit], dp[K][i][bit]);
    }
    return res;
}

int solve_testcase() {
    cin >> D >> K >> L;
    if(D == 0 and K == 0 and L == 0) return 1;
    for(int i=0; i<D; i++) {
        for(int j=0; j<K; j++) {
            cin >> c[i][j];
        }
    }

    // solve (DP);
    vector<int> vec(shift[K], INF);
    vec[0] = 0;
    for(int i=0; i<D; i++) {
        vec = solve_dp(i, vec);
    }

    /*
      for(int i=0; i<shift[K]; i++) {
      fprintf(stderr, "vec[%d] = %d\n", i, vec[i]);
      }
    */

    cin >> M >> N >> P;
    for(int i=0; i<M; i++) {
        for(int j=0; j<K; j++) {
            cin >> r[i][j];
        }
    }

    for(int i=0; i<P; i++) {
        for(int j=0; j<K; j++) {
            cin >> t[i][j];
        }
    }
  
    Flow fl(M + P + 2);
    int source = M + P, sink = source + 1;
    for(int i=0; i<P; i++) fl.add_edge(source, i, 1, 0);
    for(int i=0; i<M; i++) fl.add_edge(P+i, sink, 1, 0);

    // fprintf(stderr, "source sink end\n");
    vector<int> cnt(K);
    for(int i=0; i<P; i++) {
        for(int j=0; j<M; j++) {
            fill(cnt.begin(), cnt.end(), 0);

            // fprintf(stderr, "cnt = %zu\n", cnt.size());
            // fprintf(stderr, "fill end\n");
            for(int k=0; k<K; k++) cnt[k] += r[j][k];
            for(int k=0; k<K; k++) cnt[k] -= t[i][k];

            bool ok = true;
            for(int k=0; k<K; k++) ok &= (cnt[k] >= 0);
            if(!ok) continue;

            // fprintf(stderr, "before encode\n");
            int bit = encode(cnt);
            // fprintf(stderr, "bit = %d, size = %zu\n", bit, vec.size());
            if(vec[bit] == INF) continue;
            // fprintf(stderr, "add edge: [%d, %d, %d]\n", i, j, vec[bit]);
            fl.add_edge(i, P+j, 1, vec[bit]);
        }
    }

    for(int i=0; i<M; i++) {
        fill(cnt.begin(), cnt.end(), 0);
        for(int k=0; k<K; k++) cnt[k] += r[i][k];
        int bit = encode(cnt);
        if(vec[bit] == INF) continue;
        // fprintf(stderr, "# add edge: [source, %d, %d]\n", i, vec[bit]);
        fl.add_edge(source, P+i, 1, vec[bit]);
    }

    // fprintf(stderr, "before flow\n");
    int ans = fl.mincost_flow(source, sink, N);
    cout << ans << endl;
    return 0;
}

int main() {
    shift[0] = 1;
    for(int i=1; i<10; i++) shift[i] = shift[i-1] * 3;
  
    while(!solve_testcase());
}
accuracy: 1
time_ms: 1470
memory_kb: 5332
final_rank: 9

code_snippet: // 基本テンプレート
 
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <string>
#include <cstring>
#include <deque>
#include <list>
#include <queue>
#include <stack>
#include <vector>
#include <utility>
#include <algorithm>
#include <map>
#include <set>
#include <complex>
#include <cmath>
#include <limits>
#include <cfloat>
#include <climits>
#include <ctime>
#include <cassert>
#include <numeric>
#include <fstream>
#include <functional>
using namespace std;
 
#define rep(i,a,n) for(int (i)=(a); (i)<(n); (i)++)
#define repq(i,a,n) for(int (i)=(a); (i)<=(n); (i)++)
#define repr(i,a,n) for(int (i)=(a); (i)>=(n); (i)--)
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define int long long int
 
template<typename T> void chmax(T &a, T b) {a = max(a, b);}
template<typename T> void chmin(T &a, T b) {a = min(a, b);}
template<typename T> void chadd(T &a, T b) {a = a + b;}
 
typedef pair<int, int> pii;
typedef long long ll;
 
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
const ll INF = 1001001001001001LL;
const ll MOD = 1000000007LL;

int rec[210][210], dp[6600];
int memo[6600];
 
int D, K, L, M, N, P;
int c[10][10], r[210][10], t[210][10];

// Ford-Fulkerson 法による 最大流 O( F |E| )
// Bellman-Ford 法による 最小費用流 O( F |V| |E| )
// Verified: AOJ GRL_6_A (Maximum Flow)

// 行き先と容量と逆辺のインデックスを記録する構造体
// 通常のグラフの辺の構造体と異なるため注意

// 最小費用流はもう少し速くできるので、改良しましょう
// → ダイクストラが使えるようにポテンシャルを導入しよう

template <typename T>
struct Edge {
    int to; T cap, cost; int rev;
    Edge(int t, T c, int r) : to(t), cap(c), rev(r) {}
    Edge(int t, T ca, T co, int r) : to(t), cap(ca), cost(co), rev(r) {}
};

template <typename T>
using Graph = vector< vector< Edge<T> > >;

template <typename T>
struct Elem {
    int pos;
    T cost;
    Elem() {}
    Elem(int a, T b) : pos(a), cost(b) {}
    bool operator<(const Elem &e) const {
        return cost > e.cost;
    }
};

template <typename T>
struct Flow {
    vector< vector< Edge<T> > > G;
    const T MAXC = 1 << 30;
    int n;
    vector<bool> used;
    vector<int> prevv, preve, dist, pot;
    Flow(int _n) : G(_n), n(_n), used(_n, false), 
        prevv(n), preve(n), dist(n, MAXC), pot(n) {}

    // G[e.to][e.rev] で逆辺を操作できる
    void add_edge(int from, int to, T cap) {
        G[from].push_back(Edge<T>(to, cap, G[to].size()));
        G[to].push_back(Edge<T>(from, 0, G[from].size() - 1));
    }
    void add_edge(int from, int to, T cap, T cost) {
        G[from].push_back(Edge<T>(to, cap, cost, G[to].size()));
        G[to].push_back(Edge<T>(from, 0, -cost, G[from].size() - 1));
    }

    T dfs(int v, int t, T f) {
        if(v == t) return f;
        used[v] = true;
        for(int i=0; i < G[v].size(); i++) {
            Edge<T> &e = G[v][i];
            if(!used[e.to] && e.cap > 0) {
                T d = dfs(e.to, t, min(f, e.cap));
                if(d > 0) {
                    e.cap -= d;
                    G[e.to][e.rev].cap += d;
                    return d;
                }
            }
        }
        return 0;
    }

    T max_flow(int s, int t) {
        T flow = 0;
        while(1) {
            fill(used.begin(), used.end(), false);
            T f = dfs(s, t, INF);
            if(f == 0) return flow;
            flow += f;
        }
    }

    
    T mincost_flow(int s, int t, T f) {
        T res = 0;
        T ma = MAXC;
        while(f > 0) {
            fill(dist.begin(), dist.end(), ma);
            dist[s] = 0;
            bool update = true;
            while(update) {
                update = false;
                for(int v = 0; v < n; v++) {
                    if(dist[v] == ma) continue;
                    for(int i=0; i<G[v].size(); i++) {
                        Edge<T> &e = G[v][i];
                        if(e.cap>0 && dist[e.to] > dist[v] + e.cost) {
                            dist[e.to] = dist[v] + e.cost;
                            prevv[e.to] = v;
                            preve[e.to] = i;
                            update = true;
                        }
                    }
                }
            }

            if(dist[t] == ma) return -1;
            T d = f;
            for(int v = t; v != s; v = prevv[v]) {
                d = min(d, G[prevv[v]][preve[v]].cap);
            }
            f -= d;
            res += d * dist[t];
            for(int v = t; v != s; v = prevv[v]) {
                Edge<T> &e = G[prevv[v]][preve[v]];
                e.cap -= d;
                G[v][e.rev].cap += d;
            }
        }
        return res;
    }
    

    /*
    T mincost_flow(int s, int t, T f) {
        T res = 0;
        fill(pot.begin(), pot.end(), 0);
        while(f > 0) {
            priority_queue< Elem<T> > que;
            fill(dist.begin(), dist.end(), INF);

            dist[s] = 0;
            que.push(Elem<T>(s, 0));

            while(que.size()) {
                Elem<T> cur = que.top(); que.pop();
                int v = cur.pos;
                if(dist[v] < cur.cost) continue;
                for(size_t i=0; i<G[v].size(); i++) {
                    Edge<T> &e = G[v][i];
                    if(e.cap > 0 && dist[e.to] > dist[v] + e.cost + pot[v] - pot[e.to]) {
                        dist[e.to] = dist[v] + e.cost + pot[v] - pot[e.to];
                        prevv[e.to] = v;
                        preve[e.to] = i;
                        que.push(Elem<T>(e.to, dist[e.to]));
                    }
                }
            }

            if(dist[t] == INF) return -1;
            for(int v=0; v<n; v++) pot[v] += dist[v];

            T d = f;
            for(int v=t; v!=s; v=prevv[v]) {
                d = min(d, G[prevv[v]][preve[v]].cap);
            }
            f -= d;
            res += d * pot[t];

            for(int v=t; v!=s; v=prevv[v]) {
                Edge<T> &e = G[prevv[v]][preve[v]];
                e.cap -= d;
                G[v][e.rev].cap += d;
            }
        }
        return res;
    }
    */
};

string int2tri(int val) {
    string res = "";
    for(int i=0; i<K; i++) {
        res += (char)('0' + (val % 3));
        val /= 3;
    }
    return res;
}

int tri2int(string s) {
    reverse(s.begin(), s.end());
    int res = 0;
    for(size_t i=0; i<s.length(); i++) {
        res *= 3;
        res += (s[i] - '0');
    }
    return res;
}

signed main() {
    while(cin >> D >> K >> L, D || K || L) {
        int pow3 = 1;
        for(int i=0; i<K; i++) pow3 *= 3;

        for(int i=0; i<D; i++) {
            for(int j=0; j<K; j++) {
                cin >> c[i][j];
            }
        }
        cin >> M >> N >> P;
        for(int i=0; i<M; i++) {
            for(int j=0; j<K; j++) {
                cin >> r[i][j];
            }
        }
        for(int i=0; i<P+1; i++) {
            for(int j=0; j<K; j++) {
                if(i < P) cin >> t[i][j];
                else t[i][j] = 0;
            }
        }
        P++;

        fill(memo, memo + 6600, INF);
        fill(dp, dp + 6600, INF);
        memo[0] = 0;
        for(int d=0; d<D; d++) {
            for(int j=0; j<L; j++) {
                for(int x=pow3-1; x>=0; x--) {
                    int cost = memo[x];
                    string s = int2tri(x);
                    for(int i=0; i<K; i++) {
                        if(s[i] == '2') continue;
                        s[i]++;
                        int ncost = cost + c[d][i], nx = tri2int(s);
                        if(memo[nx] > ncost) {
                            memo[nx] = ncost;
                        }
                        s[i]--;
                    }
                }
            }
        }

        for(int j=0; j<=L; j++) {
            for(int x=0; x<pow3; x++) {
                int cost = memo[x];
                chmin(dp[x], cost);
            }
        }

        /*
        for(auto x : dp) {
            fprintf(stderr, "dp[%s] = %lld\n", x.first.c_str(), x.second);
        }
        */

        for(int i=0; i<M; i++) {
            for(int j=0; j<P; j++) {
                bool ok = true;
                string target = "";
                for(int k=0; k<K; k++) {
                    if(r[i][k] < t[j][k]) ok = false;
                    target += (char)('0' + r[i][k] - t[j][k]);
                }

                if(ok) {
                    int bit = tri2int(target);
                    rec[i][j] = dp[bit];
                }
                else {
                    rec[i][j] = INF;
                }
            }
        }

        Flow<int> fl(M + P + 1);
        int source = M + P - 1, sink = source + 1;
        for(int i=0; i<M; i++) {
            fl.add_edge(source, i, 1, 0);
        }
        for(int i=0; i<P-1; i++) {
            fl.add_edge(M+i, sink, 1, 0);
        }

        for(int i=0; i<M; i++) {
            for(int j=0; j<P; j++) {
                if(rec[i][j] == INF) continue;
                if(j == P - 1) fl.add_edge(i, sink, 1, rec[i][j]);
                else           fl.add_edge(i, M+j , 1, rec[i][j]);
            }
        }

        int ans = fl.mincost_flow(source, sink, N);
        cout << ans << endl;
    }
    return 0;
}
accuracy: 1
time_ms: 3700
memory_kb: 6824
final_rank: 15

code_snippet: // 基本テンプレート
 
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <string>
#include <cstring>
#include <deque>
#include <list>
#include <queue>
#include <stack>
#include <vector>
#include <utility>
#include <algorithm>
#include <map>
#include <set>
#include <complex>
#include <cmath>
#include <limits>
#include <cfloat>
#include <climits>
#include <ctime>
#include <cassert>
#include <numeric>
#include <fstream>
#include <functional>
using namespace std;
 
#define rep(i,a,n) for(int (i)=(a); (i)<(n); (i)++)
#define repq(i,a,n) for(int (i)=(a); (i)<=(n); (i)++)
#define repr(i,a,n) for(int (i)=(a); (i)>=(n); (i)--)
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define int long long int
 
template<typename T> void chmax(T &a, T b) {a = max(a, b);}
template<typename T> void chmin(T &a, T b) {a = min(a, b);}
template<typename T> void chadd(T &a, T b) {a = a + b;}
 
typedef pair<int, int> pii;
typedef long long ll;
 
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
const ll INF = 1001001001001001LL;
const ll MOD = 1000000007LL;

int rec[210][210], dp[6600];
int memo[6600];
 
int D, K, L, M, N, P;
int c[10][10], r[210][10], t[210][10];

// Ford-Fulkerson 法による 最大流 O( F |E| )
// Bellman-Ford 法による 最小費用流 O( F |V| |E| )
// Verified: AOJ GRL_6_A (Maximum Flow)

// 行き先と容量と逆辺のインデックスを記録する構造体
// 通常のグラフの辺の構造体と異なるため注意

// 最小費用流はもう少し速くできるので、改良しましょう
// → ダイクストラが使えるようにポテンシャルを導入しよう

template <typename T>
struct Edge {
    int to; T cap, cost; int rev;
    Edge(int t, T c, int r) : to(t), cap(c), rev(r) {}
    Edge(int t, T ca, T co, int r) : to(t), cap(ca), cost(co), rev(r) {}
};

template <typename T>
using Graph = vector< vector< Edge<T> > >;

template <typename T>
struct Elem {
    int pos;
    T cost;
    Elem() {}
    Elem(int a, T b) : pos(a), cost(b) {}
    bool operator<(const Elem &e) const {
        return cost > e.cost;
    }
};

template <typename T>
struct Flow {
    vector< vector< Edge<T> > > G;
    const T MAXC = 1 << 30;
    int n;
    vector<bool> used;
    vector<int> prevv, preve, dist, pot;
    Flow(int _n) : G(_n), n(_n), used(_n, false), 
        prevv(n), preve(n), dist(n, MAXC), pot(n) {}

    // G[e.to][e.rev] で逆辺を操作できる
    void add_edge(int from, int to, T cap) {
        G[from].push_back(Edge<T>(to, cap, G[to].size()));
        G[to].push_back(Edge<T>(from, 0, G[from].size() - 1));
    }
    void add_edge(int from, int to, T cap, T cost) {
        G[from].push_back(Edge<T>(to, cap, cost, G[to].size()));
        G[to].push_back(Edge<T>(from, 0, -cost, G[from].size() - 1));
    }

    T dfs(int v, int t, T f) {
        if(v == t) return f;
        used[v] = true;
        for(int i=0; i < G[v].size(); i++) {
            Edge<T> &e = G[v][i];
            if(!used[e.to] && e.cap > 0) {
                T d = dfs(e.to, t, min(f, e.cap));
                if(d > 0) {
                    e.cap -= d;
                    G[e.to][e.rev].cap += d;
                    return d;
                }
            }
        }
        return 0;
    }

    T max_flow(int s, int t) {
        T flow = 0;
        while(1) {
            fill(used.begin(), used.end(), false);
            T f = dfs(s, t, INF);
            if(f == 0) return flow;
            flow += f;
        }
    }

    /*
    T mincost_flow(int s, int t, T f) {
        T res = 0;
        T ma = MAXC;
        while(f > 0) {
            fill(dist.begin(), dist.end(), ma);
            dist[s] = 0;
            bool update = true;
            while(update) {
                update = false;
                for(int v = 0; v < n; v++) {
                    if(dist[v] == ma) continue;
                    for(int i=0; i<G[v].size(); i++) {
                        Edge<T> &e = G[v][i];
                        if(e.cap>0 && dist[e.to] > dist[v] + e.cost) {
                            dist[e.to] = dist[v] + e.cost;
                            prevv[e.to] = v;
                            preve[e.to] = i;
                            update = true;
                        }
                    }
                }
            }

            if(dist[t] == ma) return -1;
            T d = f;
            for(int v = t; v != s; v = prevv[v]) {
                d = min(d, G[prevv[v]][preve[v]].cap);
            }
            f -= d;
            res += d * dist[t];
            for(int v = t; v != s; v = prevv[v]) {
                Edge<T> &e = G[prevv[v]][preve[v]];
                e.cap -= d;
                G[v][e.rev].cap += d;
            }
        }
        return res;
    }
    */

    T mincost_flow(int s, int t, T f) {
        T res = 0;
        fill(pot.begin(), pot.end(), 0);
        while(f > 0) {
            priority_queue< Elem<T> > que;
            fill(dist.begin(), dist.end(), INF);

            dist[s] = 0;
            que.push(Elem<T>(s, 0));

            while(que.size()) {
                Elem<T> cur = que.top(); que.pop();
                int v = cur.pos;
                if(dist[v] < cur.cost) continue;
                for(size_t i=0; i<G[v].size(); i++) {
                    Edge<T> &e = G[v][i];
                    if(e.cap > 0 && dist[e.to] > dist[v] + e.cost + pot[v] - pot[e.to]) {
                        dist[e.to] = dist[v] + e.cost + pot[v] - pot[e.to];
                        prevv[e.to] = v;
                        preve[e.to] = i;
                        que.push(Elem<T>(e.to, dist[e.to]));
                    }
                }
            }

            if(dist[t] == INF) return -1;
            for(int v=0; v<n; v++) pot[v] += dist[v];

            T d = f;
            for(int v=t; v!=s; v=prevv[v]) {
                d = min(d, G[prevv[v]][preve[v]].cap);
            }
            f -= d;
            res += d * pot[t];

            for(int v=t; v!=s; v=prevv[v]) {
                Edge<T> &e = G[prevv[v]][preve[v]];
                e.cap -= d;
                G[v][e.rev].cap += d;
            }
        }
        return res;
    }
};

string int2tri(int val) {
    string res = "";
    for(int i=0; i<K; i++) {
        res += (char)('0' + (val % 3));
        val /= 3;
    }
    return res;
}

int tri2int(string s) {
    reverse(s.begin(), s.end());
    int res = 0;
    for(size_t i=0; i<s.length(); i++) {
        res *= 3;
        res += (s[i] - '0');
    }
    return res;
}

signed main() {
    while(cin >> D >> K >> L, D || K || L) {
        int pow3 = 1;
        for(int i=0; i<K; i++) pow3 *= 3;

        for(int i=0; i<D; i++) {
            for(int j=0; j<K; j++) {
                cin >> c[i][j];
            }
        }
        cin >> M >> N >> P;
        for(int i=0; i<M; i++) {
            for(int j=0; j<K; j++) {
                cin >> r[i][j];
            }
        }
        for(int i=0; i<P+1; i++) {
            for(int j=0; j<K; j++) {
                if(i < P) cin >> t[i][j];
                else t[i][j] = 0;
            }
        }
        P++;

        fill(memo, memo + 6600, INF);
        fill(dp, dp + 6600, INF);
        memo[0] = 0;
        for(int d=0; d<D; d++) {
            for(int j=0; j<L; j++) {
                for(int x=pow3-1; x>=0; x--) {
                    int cost = memo[x];
                    string s = int2tri(x);
                    for(int i=0; i<K; i++) {
                        if(s[i] == '2') continue;
                        s[i]++;
                        int ncost = cost + c[d][i], nx = tri2int(s);
                        if(memo[nx] > ncost) {
                            memo[nx] = ncost;
                        }
                        s[i]--;
                    }
                }
            }
        }

        for(int j=0; j<=L; j++) {
            for(int x=0; x<pow3; x++) {
                int cost = memo[x];
                chmin(dp[x], cost);
            }
        }

        /*
        for(auto x : dp) {
            fprintf(stderr, "dp[%s] = %lld\n", x.first.c_str(), x.second);
        }
        */

        for(int i=0; i<M; i++) {
            for(int j=0; j<P; j++) {
                bool ok = true;
                string target = "";
                for(int k=0; k<K; k++) {
                    if(r[i][k] < t[j][k]) ok = false;
                    target += (char)('0' + r[i][k] - t[j][k]);
                }

                if(ok) {
                    int bit = tri2int(target);
                    rec[i][j] = dp[bit];
                }
                else {
                    rec[i][j] = INF;
                }
            }
        }

        Flow<int> fl(M + P + 1);
        int source = M + P - 1, sink = source + 1;
        for(int i=0; i<M; i++) {
            fl.add_edge(source, i, 1, 0);
        }
        for(int i=0; i<P-1; i++) {
            fl.add_edge(M+i, sink, 1, 0);
        }

        for(int i=0; i<M; i++) {
            for(int j=0; j<P; j++) {
                if(rec[i][j] == INF) continue;
                if(j == P - 1) fl.add_edge(i, sink, 1, rec[i][j]);
                else           fl.add_edge(i, M+j , 1, rec[i][j]);
            }
        }

        int ans = fl.mincost_flow(source, sink, N);
        cout << ans << endl;
    }
    return 0;
}
accuracy: 1
time_ms: 3730
memory_kb: 6716
final_rank: 16

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



#define NUM 450

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

//辺を表す構造体{行先、容量、コスト、逆辺のインデックス}
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,capacity,cost,rev_index;
};

int V; //頂点数
vector<Edge> G[NUM]; //グラフの隣接リスト表現
int h[NUM]; //ポテンシャル
int dist[NUM]; //最短距離
int pre_node[NUM],pre_edge[NUM]; //直前の頂点と辺
int num_day,num_item,num_limit;
int num_problem,num_student,num_bag;
int POW[9];
int price[8][8],problem[200][8],bag[200][8];
int dp[9][6561][9];
int min_cost[6561];
int bag_index[200],problem_index[200];



//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));
}

//sourceからsinkへの、流量flowの最小費用流を求める
//流せない場合は-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 -1;
		}

		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;
		ret += tmp_flow*h[sink];
		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;
}

void func(){

	//商品情報を取得
	for(int i = 0; i < num_day; i++){
		for(int k = 0; k < num_item; k++)scanf("%d",&price[i][k]);
	}

	scanf("%d %d %d",&num_problem,&num_student,&num_bag);

	for(int i = 0; i < num_problem; i++){
		for(int k = 0; k < num_item; k++)scanf("%d",&problem[i][k]);
	}

	for(int i = 0; i < num_bag; i++){
		for(int k = 0; k < num_item; k++)scanf("%d",&bag[i][k]);
	}

	for(int state = 0; state < POW[num_item]; state++)min_cost[state] = BIG_NUM; //部品をstateの組合せで買う時の最小コスト

	for(int day = 0; day <= num_day; day++){
		for(int state = 0; state < POW[num_item]; state++){
			for(int i = 0; i <= num_limit; i++)dp[day][state][i] = BIG_NUM;
		}
	}

	dp[0][0][0] = 0;
	min_cost[0] = 0;

	int next_state,next_cost,tmp;

	//stateの組合せの部品を揃える際の最小コストを求める
	for(int day = 0; day <= num_day-1; day++){
		for(int state = 0; state < POW[num_item]; state++){
			for(int bought_num = 0; bought_num <= num_limit; bought_num++){

				if(dp[day][state][bought_num] == BIG_NUM)continue;

				dp[day+1][state][0] = min(dp[day+1][state][0],dp[day][state][bought_num]);

				if(bought_num == num_limit)continue; //もう買えないので、dp値を伝播させたらSKIP

				for(int i = 0; i < num_item; i++){
					if((state/POW[i])%3 == 2)continue;

					next_state = state+POW[i];
					next_cost = dp[day][state][bought_num]+price[day][i];

					dp[day][next_state][bought_num+1] = min(dp[day][next_state][bought_num+1],next_cost);
					min_cost[next_state] = min(min_cost[next_state],next_cost);
				}
			}
		}
	}

	for(int i = 0; i < NUM; i++)G[i].clear();

	int source = 0, sink = 1, empty_bag = 2, index = 3;

	//袋の採番
	for(int i = 0; i < num_bag; i++)bag_index[i] = index++;
	//課題の採番
	for(int i = 0; i < num_problem; i++)problem_index[i] = index++;

	//sourceから袋への辺張り
	add_edge(source,empty_bag,200,0); //★★空袋はいくつ割り当てても良い★★
	for(int i = 0; i < num_bag; i++){
		add_edge(source,bag_index[i],1,0);
	}

	//課題からsinkへの辺張り
	for(int i = 0; i < num_problem; i++){
		add_edge(problem_index[i],sink,1,0);
	}

	//袋と課題のマッチング
	int tmp_code;

	//空袋と全ての課題を繋ぐ
	for(int i = 0; i < num_problem; i++){
		tmp_code = 0;
		for(int k = 0; k < num_item; k++){
			tmp_code += problem[i][k]*POW[k];
		}
		if(min_cost[tmp_code] != BIG_NUM){
			add_edge(empty_bag,problem_index[i],1,min_cost[tmp_code]);
		}
	}

	//その他の袋と課題を繋ぐ
	bool FLG;

	for(int i = 0; i < num_bag; i++){
		for(int k = 0; k < num_problem; k++){
			FLG = true;
			for(int a = 0; a < num_item; a++){
				if(bag[i][a] > problem[k][a]){ //部品の超過は不可
					FLG = false;
					break;
				}
			}
			if(!FLG)continue;

			tmp_code = 0;
			for(int a = 0; a < num_item; a++){
				tmp_code += (problem[k][a]-bag[i][a])*POW[a];
			}
			if(min_cost[tmp_code] != BIG_NUM){
				add_edge(bag_index[i],problem_index[k],1,min_cost[tmp_code]);
			}
		}
	}

	V = index;

	printf("%d\n",min_cost_flow(source,sink,num_student));
}

int main(){

	for(int i = 0; i < 9; i++)POW[i] = pow(3,i);

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

		func();
	}
	return 0;
}
accuracy: 1
time_ms: 540
memory_kb: 6980
final_rank: 3

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



#define NUM 450

//辺を表す構造体{行先、容量、コスト、逆辺のインデックス}
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,capacity,cost,rev_index;
};

int V; //頂点数
vector<Edge> G[NUM]; //グラフの隣接リスト表現
int dist[NUM]; //最短距離
int pre_node[NUM],pre_edge[NUM]; //直前の頂点と辺
int num_day,num_item,num_limit;
int num_problem,num_student,num_bag;
int POW[9];
int price[8][8],problem[200][8],bag[200][8];
int dp[9][6561][9];
int min_cost[6561];
int bag_index[200],problem_index[200];



//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));
}

//sourceからsinkへの、流量flowの最小費用流を求める
//流せない場合は-1を返す
int min_cost_flow(int source,int sink,int flow){
	int ret = 0;
	while(flow > 0){
		//ベルマンフォード方により、source-sink間最短経路を求める
		for(int i = 0; i < V; i++)dist[i] = BIG_NUM;
		dist[source] = 0;
		bool update = true;
		while(update){
			update = false;
			for(int node_id = 0; node_id < V; node_id++){
				if(dist[node_id] == BIG_NUM)continue;
				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){
						dist[e.to] = dist[node_id]+e.cost; //node_idを経由した方が早い場合
						pre_node[e.to] = node_id;
						pre_edge[e.to] = i;
						update = true;
					}
				}
			}
		}

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

		//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;
		ret += tmp_flow*dist[sink];
		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;
}

void func(){

	//商品情報を取得
	for(int i = 0; i < num_day; i++){
		for(int k = 0; k < num_item; k++)scanf("%d",&price[i][k]);
	}

	scanf("%d %d %d",&num_problem,&num_student,&num_bag);

	for(int i = 0; i < num_problem; i++){
		for(int k = 0; k < num_item; k++)scanf("%d",&problem[i][k]);
	}

	for(int i = 0; i < num_bag; i++){
		for(int k = 0; k < num_item; k++)scanf("%d",&bag[i][k]);
	}

	for(int state = 0; state < POW[num_item]; state++)min_cost[state] = BIG_NUM; //部品をstateの組合せで買う時の最小コスト

	for(int day = 0; day <= num_day; day++){
		for(int state = 0; state < POW[num_item]; state++){
			for(int i = 0; i <= num_limit; i++)dp[day][state][i] = BIG_NUM;
		}
	}

	dp[0][0][0] = 0;
	min_cost[0] = 0;

	int next_state,next_cost,tmp;
	int work[num_item];
	stack<int> S;

	//stateの組合せの部品を揃える際の最小コストを求める
	for(int day = 0; day <= num_day-1; day++){
		for(int state = 0; state < POW[num_item]; state++){
			for(int bought_num = 0; bought_num <= num_limit; bought_num++){

				if(dp[day][state][bought_num] == BIG_NUM)continue;

				dp[day+1][state][0] = min(dp[day+1][state][0],dp[day][state][bought_num]);

				if(bought_num == num_limit)continue; //もう買えないので、dp値を伝播させたらSKIP

				//何をいくつ買ったかを計算
				tmp = state;
				for(int i = num_item-1; i >= 0; i--){
					S.push(tmp/POW[i]);
					tmp %= POW[i];
				}

				for(int i = 0; i < num_item; i++){
					work[i] = S.top();
					S.pop();
				}

				for(int i = 0; i < num_item; i++){
					if(work[i] == 2)continue;

					next_state = state+POW[i];
					next_cost = dp[day][state][bought_num]+price[day][i];

					dp[day][next_state][bought_num+1] = min(dp[day][next_state][bought_num+1],next_cost);
					min_cost[next_state] = min(min_cost[next_state],next_cost);
				}
			}
		}
	}

	for(int i = 0; i < NUM; i++)G[i].clear();

	int source = 0, sink = 1, empty_bag = 2, index = 3;

	//袋の採番
	for(int i = 0; i < num_bag; i++)bag_index[i] = index++;
	//課題の採番
	for(int i = 0; i < num_problem; i++)problem_index[i] = index++;

	//sourceから袋への辺張り
	add_edge(source,empty_bag,200,0); //★★空袋はいくつ割り当てても良い★★
	for(int i = 0; i < num_bag; i++){
		add_edge(source,bag_index[i],1,0);
	}

	//課題からsinkへの辺張り
	for(int i = 0; i < num_problem; i++){
		add_edge(problem_index[i],sink,1,0);
	}

	//袋と課題のマッチング
	int tmp_code;

	//空袋と全ての課題を繋ぐ
	for(int i = 0; i < num_problem; i++){
		tmp_code = 0;
		for(int k = 0; k < num_item; k++){
			tmp_code += problem[i][k]*POW[k];
		}
		if(min_cost[tmp_code] != BIG_NUM){
			add_edge(empty_bag,problem_index[i],1,min_cost[tmp_code]);
		}
	}

	//その他の袋と課題を繋ぐ
	bool FLG;

	for(int i = 0; i < num_bag; i++){
		for(int k = 0; k < num_problem; k++){
			FLG = true;
			for(int a = 0; a < num_item; a++){
				if(bag[i][a] > problem[k][a]){ //部品の超過は不可
					FLG = false;
					break;
				}
			}
			if(!FLG)continue;

			tmp_code = 0;
			for(int a = 0; a < num_item; a++){
				tmp_code += (problem[k][a]-bag[i][a])*POW[a];
			}
			if(min_cost[tmp_code] != BIG_NUM){
				add_edge(bag_index[i],problem_index[k],1,min_cost[tmp_code]);
			}
		}
	}

	V = index;

	printf("%d\n",min_cost_flow(source,sink,num_student));
}

int main(){

	for(int i = 0; i < 9; i++)POW[i] = pow(3,i);

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

		func();
	}
	return 0;
}
accuracy: 1
time_ms: 870
memory_kb: 7008
final_rank: 7

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



#define NUM 450

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


//辺を表す構造体{行先、容量、コスト、逆辺のインデックス}
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,capacity,cost,rev_index;
};

int V; //頂点数
vector<Edge> G[NUM]; //グラフの隣接リスト表現
int dist[NUM]; //最短距離
int pre_node[NUM],pre_edge[NUM]; //直前の頂点と辺
int num_day,num_item,num_limit;
int num_problem,num_student,num_bag;
int POW[9];
int price[8][8],problem[200][8],bag[200][8];
int dp[9][6561][9];
int min_cost[6561];
int bag_index[200],problem_index[200];



//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));
}

//sourceからsinkへの、流量flowの最小費用流を求める
//流せない場合は-1を返す
int min_cost_flow(int source,int sink,int flow){
	int ret = 0;
	while(flow > 0){
		//ベルマンフォード方により、source-sink間最短経路を求める
		for(int i = 0; i < V; i++)dist[i] = BIG_NUM;
		dist[source] = 0;
		bool update = true;
		while(update){
			update = false;
			for(int node_id = 0; node_id < V; node_id++){
				if(dist[node_id] == BIG_NUM)continue;
				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){
						dist[e.to] = dist[node_id]+e.cost; //node_idを経由した方が早い場合
						pre_node[e.to] = node_id;
						pre_edge[e.to] = i;
						update = true;
					}
				}
			}
		}

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

		//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;
		ret += tmp_flow*dist[sink];
		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;
}

void func(){

	//商品情報を取得
	for(int i = 0; i < num_day; i++){
		for(int k = 0; k < num_item; k++)scanf("%d",&price[i][k]);
	}

	scanf("%d %d %d",&num_problem,&num_student,&num_bag);

	for(int i = 0; i < num_problem; i++){
		for(int k = 0; k < num_item; k++)scanf("%d",&problem[i][k]);
	}

	for(int i = 0; i < num_bag; i++){
		for(int k = 0; k < num_item; k++)scanf("%d",&bag[i][k]);
	}

	for(int state = 0; state < POW[num_item]; state++)min_cost[state] = BIG_NUM; //部品をstateの組合せで買う時の最小コスト

	for(int day = 0; day <= num_day; day++){
		for(int state = 0; state < POW[num_item]; state++){
			for(int i = 0; i <= num_limit; i++)dp[day][state][i] = BIG_NUM;
		}
	}

	dp[0][0][0] = 0;
	min_cost[0] = 0;

	int next_state,next_cost,tmp;
	int work[num_item];
	stack<int> S;

	//stateの組合せの部品を揃える際の最小コストを求める
	for(int day = 0; day <= num_day-1; day++){
		for(int state = 0; state < POW[num_item]; state++){
			for(int bought_num = 0; bought_num <= num_limit; bought_num++){

				if(dp[day][state][bought_num] == BIG_NUM)continue;

				dp[day+1][state][0] = min(dp[day+1][state][0],dp[day][state][bought_num]);

				if(bought_num == num_limit)continue; //もう買えないので、dp値を伝播させたらSKIP

				//何をいくつ買ったかを計算
				tmp = state;
				for(int i = num_item-1; i >= 0; i--){
					S.push(tmp/POW[i]);
					tmp %= POW[i];
				}

				for(int i = 0; i < num_item; i++){
					work[i] = S.top();
					S.pop();
				}

				for(int i = 0; i < num_item; i++){
					if(work[i] == 2)continue;

					next_state = state+POW[i];
					next_cost = dp[day][state][bought_num]+price[day][i];

					dp[day][next_state][bought_num+1] = min(dp[day][next_state][bought_num+1],next_cost);
					min_cost[next_state] = min(min_cost[next_state],next_cost);
				}
			}
		}
	}

	for(int i = 0; i < NUM; i++)G[i].clear();

	int source = 0, sink = 1, empty_bag = 2, index = 3;

	//袋の採番
	for(int i = 0; i < num_bag; i++)bag_index[i] = index++;
	//課題の採番
	for(int i = 0; i < num_problem; i++)problem_index[i] = index++;

	//sourceから袋への辺張り
	add_edge(source,empty_bag,num_student,0); //★★空袋はいくつ割り当てても良い★★
	for(int i = 0; i < num_bag; i++){
		add_edge(source,bag_index[i],1,0);
	}

	//課題からsinkへの辺張り
	for(int i = 0; i < num_problem; i++){
		add_edge(problem_index[i],sink,1,0);
	}

	//袋と課題のマッチング
	int tmp_code;

	//空袋と全ての課題を繋ぐ
	for(int i = 0; i < num_problem; i++){
		tmp_code = 0;
		for(int k = 0; k < num_item; k++){
			tmp_code += problem[i][k]*POW[k];
		}
		if(min_cost[tmp_code] != BIG_NUM){
			add_edge(empty_bag,problem_index[i],1,min_cost[tmp_code]);
		}
	}

	//その他の袋と課題を繋ぐ
	bool FLG;

	for(int i = 0; i < num_bag; i++){
		for(int k = 0; k < num_problem; k++){
			FLG = true;
			for(int a = 0; a < num_item; a++){
				if(bag[i][a] > problem[k][a]){ //部品の超過は不可
					FLG = false;
					break;
				}
			}
			if(!FLG)continue;

			tmp_code = 0;
			for(int a = 0; a < num_item; a++){
				tmp_code += (problem[k][a]-bag[i][a])*POW[a];
			}
			if(min_cost[tmp_code] != BIG_NUM){
				add_edge(bag_index[i],problem_index[k],1,min_cost[tmp_code]);
			}
		}
	}

	V = index;

	printf("%d\n",min_cost_flow(source,sink,num_student));
}

int main(){

	for(int i = 0; i < 9; i++)POW[i] = pow(3,i);

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

		func();
	}
	return 0;
}
accuracy: 1
time_ms: 760
memory_kb: 7056
final_rank: 5

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



#define NUM 450

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


//辺を表す構造体{行先、容量、コスト、逆辺のインデックス}
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,capacity,cost,rev_index;
};

int V; //頂点数
vector<Edge> G[NUM]; //グラフの隣接リスト表現
int h[NUM]; //ポテンシャル
int dist[NUM]; //最短距離
int pre_node[NUM],pre_edge[NUM]; //直前の頂点と辺
int num_day,num_item,num_limit;
int num_problem,num_student,num_bag;
int POW[9];
int price[8][8],problem[200][8],bag[200][8];
int dp[9][6561][9];
int min_cost[6561];
int bag_index[200],problem_index[200];



//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));
}

//sourceからsinkへの、流量flowの最小費用流を求める
//流せない場合は-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 -1;
		}

		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;
		ret += tmp_flow*h[sink];
		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;
}

void func(){

	//商品情報を取得
	for(int i = 0; i < num_day; i++){
		for(int k = 0; k < num_item; k++)scanf("%d",&price[i][k]);
	}

	scanf("%d %d %d",&num_problem,&num_student,&num_bag);

	for(int i = 0; i < num_problem; i++){
		for(int k = 0; k < num_item; k++)scanf("%d",&problem[i][k]);
	}

	for(int i = 0; i < num_bag; i++){
		for(int k = 0; k < num_item; k++)scanf("%d",&bag[i][k]);
	}

	for(int state = 0; state < POW[num_item]; state++)min_cost[state] = BIG_NUM; //部品をstateの組合せで買う時の最小コスト

	for(int day = 0; day <= num_day; day++){
		for(int state = 0; state < POW[num_item]; state++){
			for(int i = 0; i <= num_limit; i++)dp[day][state][i] = BIG_NUM;
		}
	}

	dp[0][0][0] = 0;
	min_cost[0] = 0;

	int next_state,next_cost,tmp;
	int work[num_item];
	stack<int> S;

	//stateの組合せの部品を揃える際の最小コストを求める
	for(int day = 0; day <= num_day-1; day++){
		for(int state = 0; state < POW[num_item]; state++){
			for(int bought_num = 0; bought_num <= num_limit; bought_num++){

				if(dp[day][state][bought_num] == BIG_NUM)continue;

				dp[day+1][state][0] = min(dp[day+1][state][0],dp[day][state][bought_num]);

				if(bought_num == num_limit)continue; //もう買えないので、dp値を伝播させたらSKIP

				//何をいくつ買ったかを計算
				tmp = state;
				for(int i = num_item-1; i >= 0; i--){
					S.push(tmp/POW[i]);
					tmp %= POW[i];
				}

				for(int i = 0; i < num_item; i++){
					work[i] = S.top();
					S.pop();
				}

				for(int i = 0; i < num_item; i++){
					if(work[i] == 2)continue;

					next_state = state+POW[i];
					next_cost = dp[day][state][bought_num]+price[day][i];

					dp[day][next_state][bought_num+1] = min(dp[day][next_state][bought_num+1],next_cost);
					min_cost[next_state] = min(min_cost[next_state],next_cost);
				}
			}
		}
	}

	for(int i = 0; i < NUM; i++)G[i].clear();

	int source = 0, sink = 1, empty_bag = 2, index = 3;

	//袋の採番
	for(int i = 0; i < num_bag; i++)bag_index[i] = index++;
	//課題の採番
	for(int i = 0; i < num_problem; i++)problem_index[i] = index++;

	//sourceから袋への辺張り
	add_edge(source,empty_bag,num_student,0); //★★空袋はいくつ割り当てても良い★★
	for(int i = 0; i < num_bag; i++){
		add_edge(source,bag_index[i],1,0);
	}

	//課題からsinkへの辺張り
	for(int i = 0; i < num_problem; i++){
		add_edge(problem_index[i],sink,1,0);
	}

	//袋と課題のマッチング
	int tmp_code;

	//空袋と全ての課題を繋ぐ
	for(int i = 0; i < num_problem; i++){
		tmp_code = 0;
		for(int k = 0; k < num_item; k++){
			tmp_code += problem[i][k]*POW[k];
		}
		if(min_cost[tmp_code] != BIG_NUM){
			add_edge(empty_bag,problem_index[i],1,min_cost[tmp_code]);
		}
	}

	//その他の袋と課題を繋ぐ
	bool FLG;

	for(int i = 0; i < num_bag; i++){
		for(int k = 0; k < num_problem; k++){
			FLG = true;
			for(int a = 0; a < num_item; a++){
				if(bag[i][a] > problem[k][a]){ //部品の超過は不可
					FLG = false;
					break;
				}
			}
			if(!FLG)continue;

			tmp_code = 0;
			for(int a = 0; a < num_item; a++){
				tmp_code += (problem[k][a]-bag[i][a])*POW[a];
			}
			if(min_cost[tmp_code] != BIG_NUM){
				add_edge(bag_index[i],problem_index[k],1,min_cost[tmp_code]);
			}
		}
	}

	V = index;

	printf("%d\n",min_cost_flow(source,sink,num_student));
}

int main(){

	for(int i = 0; i < 9; i++)POW[i] = pow(3,i);

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

		func();
	}
	return 0;
}
accuracy: 1
time_ms: 800
memory_kb: 6988
final_rank: 6

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



#define NUM 450

//辺を表す構造体{行先、容量、コスト、逆辺のインデックス}
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,capacity,cost,rev_index;
};

int V; //頂点数
vector<Edge> G[NUM]; //グラフの隣接リスト表現
int dist[NUM]; //最短距離
int pre_node[NUM],pre_edge[NUM]; //直前の頂点と辺
int num_day,num_item,num_limit;
int num_problem,num_student,num_bag;
int POW[9];
int price[8][8],problem[200][8],bag[200][8];
int dp[9][6561][9];
int min_cost[6561];
int bag_index[200],problem_index[200];



//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));
}

//sourceからsinkへの、流量flowの最小費用流を求める
//流せない場合は-1を返す
int min_cost_flow(int source,int sink,int flow){
	int ret = 0;
	while(flow > 0){
		//ベルマンフォード方により、source-sink間最短経路を求める
		for(int i = 0; i < V; i++)dist[i] = BIG_NUM;
		dist[source] = 0;
		bool update = true;
		while(update){
			update = false;
			for(int node_id = 0; node_id < V; node_id++){
				if(dist[node_id] == BIG_NUM)continue;
				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){
						dist[e.to] = dist[node_id]+e.cost; //node_idを経由した方が早い場合
						pre_node[e.to] = node_id;
						pre_edge[e.to] = i;
						update = true;
					}
				}
			}
		}

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

		//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;
		ret += tmp_flow*dist[sink];
		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;
}

void func(){

	//商品情報を取得
	for(int i = 0; i < num_day; i++){
		for(int k = 0; k < num_item; k++)scanf("%d",&price[i][k]);
	}

	scanf("%d %d %d",&num_problem,&num_student,&num_bag);

	for(int i = 0; i < num_problem; i++){
		for(int k = 0; k < num_item; k++)scanf("%d",&problem[i][k]);
	}

	for(int i = 0; i < num_bag; i++){
		for(int k = 0; k < num_item; k++)scanf("%d",&bag[i][k]);
	}

	for(int state = 0; state < POW[num_item]; state++)min_cost[state] = BIG_NUM; //部品をstateの組合せで買う時の最小コスト

	for(int day = 0; day <= num_day; day++){
		for(int state = 0; state < POW[num_item]; state++){
			for(int i = 0; i <= num_limit; i++)dp[day][state][i] = BIG_NUM;
		}
	}

	dp[0][0][0] = 0;
	min_cost[0] = 0;

	int next_state,next_cost,tmp;

	//stateの組合せの部品を揃える際の最小コストを求める
	for(int day = 0; day <= num_day-1; day++){
		for(int state = 0; state < POW[num_item]; state++){
			for(int bought_num = 0; bought_num <= num_limit; bought_num++){

				if(dp[day][state][bought_num] == BIG_NUM)continue;

				dp[day+1][state][0] = min(dp[day+1][state][0],dp[day][state][bought_num]);

				if(bought_num == num_limit)continue; //もう買えないので、dp値を伝播させたらSKIP

				for(int i = 0; i < num_item; i++){
					if((state/POW[i])%3 == 2)continue;

					next_state = state+POW[i];
					next_cost = dp[day][state][bought_num]+price[day][i];

					dp[day][next_state][bought_num+1] = min(dp[day][next_state][bought_num+1],next_cost);
					min_cost[next_state] = min(min_cost[next_state],next_cost);
				}
			}
		}
	}

	for(int i = 0; i < NUM; i++)G[i].clear();

	int source = 0, sink = 1, empty_bag = 2, index = 3;

	//袋の採番
	for(int i = 0; i < num_bag; i++)bag_index[i] = index++;
	//課題の採番
	for(int i = 0; i < num_problem; i++)problem_index[i] = index++;

	//sourceから袋への辺張り
	add_edge(source,empty_bag,200,0); //★★空袋はいくつ割り当てても良い★★
	for(int i = 0; i < num_bag; i++){
		add_edge(source,bag_index[i],1,0);
	}

	//課題からsinkへの辺張り
	for(int i = 0; i < num_problem; i++){
		add_edge(problem_index[i],sink,1,0);
	}

	//袋と課題のマッチング
	int tmp_code;

	//空袋と全ての課題を繋ぐ
	for(int i = 0; i < num_problem; i++){
		tmp_code = 0;
		for(int k = 0; k < num_item; k++){
			tmp_code += problem[i][k]*POW[k];
		}
		if(min_cost[tmp_code] != BIG_NUM){
			add_edge(empty_bag,problem_index[i],1,min_cost[tmp_code]);
		}
	}

	//その他の袋と課題を繋ぐ
	bool FLG;

	for(int i = 0; i < num_bag; i++){
		for(int k = 0; k < num_problem; k++){
			FLG = true;
			for(int a = 0; a < num_item; a++){
				if(bag[i][a] > problem[k][a]){ //部品の超過は不可
					FLG = false;
					break;
				}
			}
			if(!FLG)continue;

			tmp_code = 0;
			for(int a = 0; a < num_item; a++){
				tmp_code += (problem[k][a]-bag[i][a])*POW[a];
			}
			if(min_cost[tmp_code] != BIG_NUM){
				add_edge(bag_index[i],problem_index[k],1,min_cost[tmp_code]);
			}
		}
	}

	V = index;

	printf("%d\n",min_cost_flow(source,sink,num_student));
}

int main(){

	for(int i = 0; i < 9; i++)POW[i] = pow(3,i);

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

		func();
	}
	return 0;
}
accuracy: 1
time_ms: 500
memory_kb: 6932
final_rank: 2

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



#define NUM 450

//辺を表す構造体{行先、容量、コスト、逆辺のインデックス}
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,capacity,cost,rev_index;
};

int V; //頂点数
vector<Edge> G[NUM]; //グラフの隣接リスト表現
int dist[NUM]; //最短距離
int pre_node[NUM],pre_edge[NUM]; //直前の頂点と辺
int num_day,num_item,num_limit;
int num_problem,num_student,num_bag;
int POW[9];
int price[8][8],problem[200][8],bag[200][8];
int dp[9][6561][9];
int min_cost[6561];
int bag_index[200],problem_index[200];



//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));
}

//sourceからsinkへの、流量flowの最小費用流を求める
//流せない場合は-1を返す
int min_cost_flow(int source,int sink,int flow){
	int ret = 0;
	while(flow > 0){
		//ベルマンフォード方により、source-sink間最短経路を求める
		for(int i = 0; i < V; i++)dist[i] = BIG_NUM;
		dist[source] = 0;
		bool update = true;
		while(update){
			update = false;
			for(int node_id = 0; node_id < V; node_id++){
				if(dist[node_id] == BIG_NUM)continue;
				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){
						dist[e.to] = dist[node_id]+e.cost; //node_idを経由した方が早い場合
						pre_node[e.to] = node_id;
						pre_edge[e.to] = i;
						update = true;
					}
				}
			}
		}

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

		//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;
		ret += tmp_flow*dist[sink];
		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;
}

void func(){

	//商品情報を取得
	for(int i = 0; i < num_day; i++){
		for(int k = 0; k < num_item; k++)scanf("%d",&price[i][k]);
	}

	scanf("%d %d %d",&num_problem,&num_student,&num_bag);

	for(int i = 0; i < num_problem; i++){
		for(int k = 0; k < num_item; k++)scanf("%d",&problem[i][k]);
	}

	for(int i = 0; i < num_bag; i++){
		for(int k = 0; k < num_item; k++)scanf("%d",&bag[i][k]);
	}

	for(int state = 0; state < POW[num_item]; state++)min_cost[state] = BIG_NUM; //部品をstateの組合せで買う時の最小コスト

	for(int day = 0; day <= num_day; day++){
		for(int state = 0; state < POW[num_item]; state++){
			for(int i = 0; i <= num_limit; i++)dp[day][state][i] = BIG_NUM;
		}
	}

	dp[0][0][0] = 0;
	min_cost[0] = 0;

	int next_state,next_cost,tmp;
	int work[num_item];

	stack<int> S;

	//stateの組合せの部品を揃える際の最小コストを求める
	for(int day = 0; day <= num_day-1; day++){
		for(int state = 0; state < POW[num_item]; state++){
			for(int bought_num = 0; bought_num <= num_limit; bought_num++){

				if(dp[day][state][bought_num] == BIG_NUM)continue;

				dp[day+1][state][0] = min(dp[day+1][state][0],dp[day][state][bought_num]);

				if(bought_num == num_limit)continue; //もう買えないので、dp値を伝播させたらSKIP

				//何をいくつ買ったかを計算
				tmp = state;
				for(int i = num_item-1; i >= 0; i--){
					S.push(tmp/POW[i]);
					tmp %= POW[i];
				}

				for(int i = 0; i < num_item; i++){
					if(work[i] == 2)continue;

					next_state = state+POW[i];
					next_cost = dp[day][state][bought_num]+price[day][i];

					dp[day][next_state][bought_num+1] = min(dp[day][next_state][bought_num+1],next_cost);
					min_cost[next_state] = min(min_cost[next_state],next_cost);
				}
			}
		}
	}

	for(int i = 0; i < NUM; i++)G[i].clear();

	int source = 0, sink = 1, empty_bag = 2, index = 3;

	//袋の採番
	for(int i = 0; i < num_bag; i++)bag_index[i] = index++;
	//課題の採番
	for(int i = 0; i < num_problem; i++)problem_index[i] = index++;

	//sourceから袋への辺張り
	add_edge(source,empty_bag,200,0); //★★空袋はいくつ割り当てても良い★★
	for(int i = 0; i < num_bag; i++){
		add_edge(source,bag_index[i],1,0);
	}

	//課題からsinkへの辺張り
	for(int i = 0; i < num_problem; i++){
		add_edge(problem_index[i],sink,1,0);
	}

	//袋と課題のマッチング
	int tmp_code;

	//空袋と全ての課題を繋ぐ
	for(int i = 0; i < num_problem; i++){
		tmp_code = 0;
		for(int k = 0; k < num_item; k++){
			tmp_code += problem[i][k]*POW[k];
		}
		if(min_cost[tmp_code] != BIG_NUM){
			add_edge(empty_bag,problem_index[i],1,min_cost[tmp_code]);
		}
	}

	//その他の袋と課題を繋ぐ
	bool FLG;

	for(int i = 0; i < num_bag; i++){
		for(int k = 0; k < num_problem; k++){
			FLG = true;
			for(int a = 0; a < num_item; a++){
				if(bag[i][a] > problem[k][a]){ //部品の超過は不可
					FLG = false;
					break;
				}
			}
			if(!FLG)continue;

			tmp_code = 0;
			for(int a = 0; a < num_item; a++){
				tmp_code += (problem[k][a]-bag[i][a])*POW[a];
			}
			if(min_cost[tmp_code] != BIG_NUM){
				add_edge(bag_index[i],problem_index[k],1,min_cost[tmp_code]);
			}
		}
	}

	V = index;

	printf("%d\n",min_cost_flow(source,sink,num_student));
}

int main(){

	for(int i = 0; i < 9; i++)POW[i] = pow(3,i);

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

		func();
	}
	return 0;
}
accuracy: 0.125
time_ms: 30
memory_kb: 12036
final_rank: 17

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

typedef pair<int, int> P;

struct edge {
    int to, cap, cost, rev;
};

int V;
vector<edge> G[50200];
int h[50200], dist[50200];
int prevv[50200], preve[50200];

void addEdge(int from, int to, int cap, int cost)
{
    V = max(V, max(from + 1, to + 1));
    G[from].push_back((edge){to, cap, cost, G[to].size()});
    G[to].push_back((edge){from, 0, -cost, G[from].size() - 1});
}

int minCostFlow(int s, int t, int f)
{
    int res = 0;
    fill(h, h + V, 0);
    while (f > 0){
        priority_queue<P, vector<P>, greater<P> > pq;
        fill(dist, dist + V, 1000000000);
        dist[s] = 0; pq.push(P(0, s));
        while (!pq.empty()){
            P p = pq.top(); pq.pop();
            int v = p.second;
            if (dist[v] < p.first) continue;
            for (int i = 0; i < G[v].size(); i++){
                edge &e = G[v][i];
                if (e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]){
                    dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
                    prevv[e.to] = v;
                    preve[e.to] = i;
                    pq.push(P(dist[e.to], e.to));
                }
            }
        }

        if (dist[t] == 1000000000) return (-1);
        for (int v = 0; v < V; v++) h[v] += dist[v];

        int d = f;
        for (int v = t; v != s; v = prevv[v]){
            d = min(d, G[prevv[v]][preve[v]].cap);
        }
        f -= d;
        res += d * h[t];
        for (int v = t; v != s; v = prevv[v]){
            edge &e = G[prevv[v]][preve[v]];
            e.cap -= d;
            G[v][e.rev].cap += d;
        }
    }
    return (res);
}

int encode(int *p, int N)
{
    int ret = 0;
    for (int i = N - 1; i >= 0; i--)
        ret = ret * 3 + p[i];
    return (ret);
}

void decode(int bit, int N, int *p)
{
    for (int i = 0; i < N; i++){
        p[i] = bit % 3; bit /= 3;
    }
}

int main()
{
    int D, K, L;
    int M, N, P;
    int pow3[10];

    pow3[0] = 1;
    for (int i = 1; i <= 9; i++) pow3[i] = pow3[i - 1] * 3;

    while (scanf("%d %d %d", &D, &K, &L) && D){
        int c[8][8];
        for (int i = 0; i < D; i++)
            for (int j = 0; j < K; j++)
                scanf("%d", &c[i][j]);

        scanf("%d %d %d", &M, &N, &P);

        int tar[256][8];
        memset(tar, 0, sizeof(tar));
        for (int i = 0; i < M; i++)
            for (int j = 0; j < K; j++)
                scanf("%d", &tar[i][j]);

        P++;
        for (int i = 0; i < M + P + 2; i++) G[i].clear();

        int dp[9][6561][9];
            for (int i = 0; i < 9; i++)
                for (int j = 0; j < 6561; j++)
                    for (int k = 0; k < 9; k++)
                        dp[i][j][k] = 1000000000;
            dp[0][0][0] = 0;

            int J = pow3[K];
            for (int i = 0; i < D; i++){
                for (int j = 0; j < J; j++){
                    for (int k = 0; k <= L; k++)
                        dp[i + 1][j][0] = min(dp[i + 1][j][0], dp[i][j][k]);
                    for (int k = 0; k < L; k++){
                        for (int l = 0; l < K; l++){
                            if ((j / pow3[l]) % 3 < 2)
                                dp[i + 1][j + pow3[l]][k + 1] = min(dp[i + 1][j + pow3[l]][k + 1], dp[i + 1][j][k] + c[i][l]);
                        }
                    }
                }
            }

        for (int s = 0; s < P; s++){
            int t[8];

            if (s != P - 1) for (int i = 0; i < K; i++) scanf("%d", t + i);
            else for (int i = 0; i < K; i++) t[i] = 0;

            for (int i = 0; i < M; i++){
                if (!s) addEdge(P + i + 1, P + M + 1, 1, 0);

                int mini = 1000000000;
                int tt[8];
                bool flag = false;
                for (int j = 0; j < K; j++) if (tar[i][j] < t[j]) flag = true;
                if (flag) continue;
                for (int j = 0; j < K; j++) tt[j] = tar[i][j] - t[j];
                int x = encode(tt, K);
                for (int j = 0; j <= L; j++) mini = min(mini, dp[D][x][j]);
                if (mini != 1000000000){
                    addEdge(s + 1, P + i + 1, 1, mini);
                    //printf("%d -> %d cost %d\n", s + 1, P + i + 1, mini);
                }
            }
            addEdge(0, s + 1, 1 + 1000000000 * (s == P - 1), 0);
        }

       printf("%d\n", minCostFlow(0, M + P + 1, N));
    }

    return (0);
}
accuracy: 1
time_ms: 490
memory_kb: 7892
final_rank: 4

code_snippet: #include "bits/stdc++.h"
using namespace std;
#ifdef _DEBUG
#include "dump.hpp"
#else
#define dump(...)
#endif

//#define int long long
#define rep(i,a,b) for(int i=(a);i<(b);i++)
#define rrep(i,a,b) for(int i=(b)-1;i>=(a);i--)
#define all(c) begin(c),end(c)
const int INF = sizeof(int) == sizeof(long long) ? 0x3f3f3f3f3f3f3f3fLL : 0x3f3f3f3f;
const int MOD = (int)(1e9) + 7;
template<class T> bool chmax(T &a, const T &b) { if (a < b) { a = b; return true; } return false; }
template<class T> bool chmin(T &a, const T &b) { if (b < a) { a = b; return true; } return false; }

struct MinimumCostFlow {
	using Flow = int;
	using Cost = int;
	const Cost kInfCost = INF;
	struct Edge {
		int to, rev;
		Flow cap;
		Cost cost;
		Edge() {}
		Edge(int to, int rev, Flow cap, Cost cost) :to(to), rev(rev), cap(cap), cost(cost) {}
	};
	int n;
	vector<vector<Edge>> g;
	vector<Cost> dist;
	vector<int> prevv, preve;
	MinimumCostFlow(int n) :n(n), g(n), dist(n), prevv(n), preve(n) {}
	void addArc(int from, int to, Flow cap, Cost cost) {
		g[from].emplace_back(to, (int)g[to].size(), cap, cost);
		g[to].emplace_back(from, (int)g[from].size() - 1, 0, -cost);
	}
	// s??????t????????????f???????°??????¨???
	// ??????????????´?????? -1
	Cost minimumCostFlow(int s, int t, Flow f) {
		Cost total = 0;
		while (f > 0) {
			// Bellman-Ford
			fill(dist.begin(), dist.end(), kInfCost);
			dist[s] = 0;
			bool update = true;
			while (update) {
				update = false;
				for (int v = 0; v < n; v++) {
					if (dist[v] == kInfCost)continue;
					for (int i = 0; i < g[v].size(); i++) {
						Edge &e = g[v][i];
						if (e.cap > 0 && dist[e.to] > dist[v] + e.cost) {
							dist[e.to] = dist[v] + e.cost;
							prevv[e.to] = v;
							preve[e.to] = i;
							update = true;
						}
					}
				}
			}
			// ????????\???????????????
			if (dist[t] == kInfCost)
				return -1;
			// ?????????????????£??????????????????
			Flow d = f;
			for (int v = t; v != s; v = prevv[v])
				d = min(d, g[prevv[v]][preve[v]].cap);
			f -= d;
			total += dist[t] * d;
			for (int v = t; v != s; v = prevv[v]) {
				Edge &e = g[prevv[v]][preve[v]];
				e.cap -= d;
				g[v][e.rev].cap += d;
			}
		}
		return total;
	}
};

signed main() {
	cin.tie(0);
	ios::sync_with_stdio(false);
	for (int D, K, L; cin >> D >> K >> L&&D;) {
		vector<vector<int>> c(D, vector<int>(K));
		rep(i, 0, D) rep(j, 0, K) { cin >> c[i][j]; }
		int M, N, P; cin >> M >> N >> P;
		vector<vector<int>> r(M, vector<int>(K));
		rep(i, 0, M) rep(j, 0, K) { cin >> r[i][j]; }
		vector<vector<int>> t(P + 1, vector<int>(K));
		rep(i, 0, P) rep(j, 0, K) { cin >> t[i][j]; }

		P++;
		MinimumCostFlow mcf1(M + P + 2);
		int s1 = mcf1.n - 2, t1 = s1 + 1;

		rep(i, 0, P) {
			rep(j, 0, M) {
				bool ok = true;
				rep(k, 0, K) {
					if (r[j][k] < t[i][k])
						ok = false;
				}
				if (ok) {
					MinimumCostFlow mcf2(D + K + 2);
					int s2 = mcf2.n - 2, t2 = s2 + 1;
					int sum = 0;
					rep(d, 0, D) {
						rep(k, 0, K) {
							mcf2.addArc(d, D + k, L, c[d][k]);
						}
					}
					rep(d, 0, D) {
						mcf2.addArc(s2, d, L, 0);
					}
					rep(k, 0, K) {
						if (r[j][k] > t[i][k]) {
							mcf2.addArc(D + k, t2, r[j][k] - t[i][k], 0);
							sum += r[j][k] - t[i][k];
						}
					}
					auto res = mcf2.minimumCostFlow(s2, t2, sum);
					if (res != -1) {
						mcf1.addArc(i, P + j, 1, res);
					}
				}
			}
		}
		rep(i, 0, P) {
			if (i == P - 1)
				mcf1.addArc(s1, i, INF, 0);
			else
				mcf1.addArc(s1, i, 1, 0);
		}
		rep(i, 0, M) {
			mcf1.addArc(P + i, t1, 1, 0);
		}
		cout << mcf1.minimumCostFlow(s1, t1, N) << endl;
	}

	return 0;
}
accuracy: 1
time_ms: 460
memory_kb: 4532
final_rank: 1

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define int long long
#define all(v) begin(v), end(v)
#define rep(i, n) for(int i = 0; i < (int)(n); i++)
#define reps(i, s, n) for(int i = (int)(s); i < (int)(n); i++)
#define min(...) min({__VA_ARGS__})
#define max(...) max({__VA_ARGS__})

const int inf = 1LL << 55;
const int mod = 1e9 + 7;

// Sccessive Shortest Path(Primal Dual): minimum cost maximum flow
struct PrimalDual
{
  struct edge
  {
    int to, capacity, cost, rev;
    edge(){}
    edge(int to, int capacity, int cost, int rev):to(to), capacity(capacity), cost(cost), rev(rev){}
  };

  vector< vector<edge> > graph;
  vector<int> potential, mincost, prevv, preve;
  PrimalDual(int V):graph(V), potential(V), mincost(V), prevv(V), preve(V){}
  void add_edge(int from, int to, int capacity, int cost)
  {
    graph[from].push_back(edge(to, capacity, cost, (int)graph[to].size()));
    graph[to].push_back(edge(from, 0, -cost, (int)graph[from].size()-1));
  }
  int min_cost_flow(int source, int sink, int f)
  {
    int res = 0;
    fill(potential.begin(), potential.end(), 0);
    fill(prevv.begin(), prevv.end(), -1);
    fill(preve.begin(), preve.end(), -1);

    while(f > 0) {
      typedef pair<int, int> Pi;
      priority_queue<Pi, vector<Pi>, greater<Pi> > que;
      fill(mincost.begin(), mincost.end(), inf);
      mincost[source] = 0;
      que.push(Pi(0, source));
      while(!que.empty()) {
	Pi p = que.top(); que.pop();
	int v = p.second;
	if(mincost[v] < p.first) continue;
	for(int i = 0; i < (int)graph[v].size(); i++) {
	  edge& e = graph[v][i];
	  int dual_cost = mincost[v] + e.cost + potential[v] - potential[e.to];
	  if(e.capacity > 0 && dual_cost < mincost[e.to]) {
	    mincost[e.to] = dual_cost;
	    prevv[e.to] = v; preve[e.to] = i;
	    que.push(Pi(mincost[e.to], e.to));
	  }
	}
      }

      if(mincost[sink] == inf) return -1;
      for(int v = 0; v < (int)graph.size(); v++) potential[v] += mincost[v];
      int d = f;
      for(int v = sink; v != source; v = prevv[v]) d = min(d, graph[prevv[v]][preve[v]].capacity);
      f -= d;
      res += d * potential[sink];
      for(int v = sink; v != source; v = prevv[v]) {
	edge& e = graph[prevv[v]][preve[v]];
	e.capacity -= d;
	graph[v][e.rev].capacity += d;
      }
    }
    return res;
  }
};

int D, K, L, M, N, P;
int c[8][8];
int r[200][8], t[200][8];

int rbit[200], tbit[200]; // 1??¨????????¨???2bit???00??????0???/01??????1???/10??????2???
int dp[1<<16]; // i?????????????\???§??¨????????¶??????k?????????????°???????
// ?????????????\??????§?????¨???????????£???????????°?????????????????§???????????§???i??????????????±??????????????????

signed main()
{
  cin.tie(0);
  ios_base::sync_with_stdio(0);
  cout << fixed << setprecision(12);

  while(cin >> D >> K >> L, D || K || L) {
    rep(i, D) rep(j, K) cin >> c[i][j];
    cin >> M >> N >> P;
    rep(i, M) rep(j, K) cin >> r[i][j];
    rep(i, P) rep(j, K) cin >> t[i][j];

    rep(i, M) {
      rbit[i] = 0;
      rep(j, K) rbit[i] |= r[i][j]<<(j*2);
    }
    rep(i, P) {
      tbit[i] = 0;
      rep(j, K) tbit[i] |= t[i][j]<<(j*2);
    }

    fill(dp, dp + (1<<16), inf);
    dp[0] = 0;
    rep(i, D) rep(j, L) { // L?????°???????????°1??\???L????????§?????£????????¨???????????????
      for(int k = (1<<K*2)-1; k >= 0; k--) { // 1??????????????????????????§????????????
	rep(l, K) if(((k>>(l*2))&3) < 2) { // 01??????1??????10??????2????????£??????
	  dp[k+(1<<(l*2))] = min(dp[k+(1<<(l*2))], dp[k] + c[i][l]);
	}
      }
    }

    int s = M + P, t = s + 1, V = t + 1;
    PrimalDual graph(V);
    rep(i, M) {
      graph.add_edge(s, i, 1, 0); // ???????????¨?????°??????????????????1
      if(dp[rbit[i]] != inf) graph.add_edge(i, t, 1, dp[rbit[i]]); // ?¢?????????§??????
    }
    rep(i, M) rep(j, P) {
      bool flag = true;
      rep(k, K) if(((rbit[i]>>(k*2))&3) < ((tbit[j]>>(k*2))&3)) flag = false; // ?¢???????????????¨??¨??????????????¨????????????
      if(flag && dp[rbit[i]-tbit[j]] != inf) graph.add_edge(i, M + j, 1, dp[rbit[i]-tbit[j]]);
    }
    rep(i, P) {
      graph.add_edge(M + i, t, 1, 0); // ?¢??????¨?????°??????????????????1
    }
    cout << graph.min_cost_flow(s, t, N) << endl;
  }

  return 0;
}
accuracy: 1
time_ms: 1440
memory_kb: 6812
final_rank: 11