s0表示哪些科目没有一个老师教,s1表示是哪些科目只有一个老师教,s2表示有哪些科目有两个老师教,d(i,s1,s2)表示考虑钱i个时的最小花费,状态转移方程d(i,s1,s2)=min{d(i+1,s1′,s2′)+c[i],d(i+1,s1,s2)} 【c[i]表示聘用这个老师的花费,s1′和s2′表示招聘这个老师后更新的新状态′
第一项表示聘用,第二项不聘用,只有i>m才出现第二项,因为在职教师不能够辞退。
#include <iostream>
#include <cstdio>
#include <string>
#include <sstream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 130;
const int INF = 1 << 20;
int s, m, n, x;
int d[maxn][1 << 8][1 << 8];
int c[maxn], st[maxn];
int dp(int i, int s0, int s1, int s2)
{
if (i == m + n)
return s2 == (1 << s) - 1 ? 0 : INF;
int &ans = d[i][s1][s2];
if (ans >= 0)
return ans;
ans = INF;
if (i >= m)
ans = dp(i + 1, s0, s1, s2);
int m0 = st[i] & s0;
int m1 = st[i] & s1;
s0 ^= m0;
s1 = (s1^m1) | m0;
s2 |= m1;
ans = min(ans, c[i] + dp(i + 1, s0, s1, s2));
return ans;
}
int main()
{
string str;
while (getline(cin, str))
{
memset(st, 0, sizeof(st));
stringstream ss(str);
ss >> s >> m >> n;
if (s == 0)
break;
for (int i = 0; i < m + n; i++)
{
getline(cin, str);
stringstream ss(str);
ss >> c[i];
while (ss >> x)
{
st[i] |= 1 << (x - 1);
}
}
memset(d, -1, sizeof(d));
cout << dp(0, (1 << s) - 1, 0, 0) << endl;
}
}