用集合s1表示一个人教的科目的集合,s2表示至少两个人教的科目的集合,s0表示无人教的科目的集合.d(i, s1, s2)表示已经考虑了前i个人时的最小花费.
状态转移方程: d(i, s1, s2) = min(d(i+1, s1, s2), c[i] + d(i+1, s1’, s2’)), s1’, s2’表示招聘了第i个人后的新值。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include <iostream>
#include <sstream>
using namespace std;
#define INF 1e9
#define maxn 9
int d[150][1<<maxn][1<<maxn], s, m, n;
int st[150], c[150];
void Init(int k, int p)
{
string s;
for(int i = 0; i < p; i++)
{
++k;
int t;
getline(cin, s);
stringstream ss(s);
ss >> t;
c[k] = t;
st[k] = 0;
while(ss >> t)
{
st[k] |= 1<<(t-1);
}
}
}
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, m1 = st[i] & s1;
s0 = s0 ^ m0, s1 = (s1 ^ m1) | m0, s2 |= m1;
ans = min(ans, c[i] + dp(i+1, s0, s1, s2));
return ans;
}
int main()
{
//freopen("in.txt", "r", stdin);
while(cin >> s >> m >> n)
{
getchar();
if(!s && !m && !n)
break;
Init(-1, m);
Init(m-1, n);
memset(d, -1, sizeof(d));
cout << dp(0, (1<<s)-1, 0, 0) << endl;
}
return 0;
}