题目大意
某校有n个教师和m个求职者,已知每人的工资和能教的课程集合,要求支付最少的工资使得每门课都至少有两名教师教学。在职教师必须招聘。
思路
d[s1][s2]: s1表示课程集合{ s1 }都至少有一个教师教的情况。
s2表示课程集合{ s2 }都至少有两个教师教的情况。
那么d[s1][s2] = min(d[s1][s2], d[s1'][s2'] + c[i]);
其中 s1 = s1' | p[i];
s2 = (s1' & p[i]) | s2';
/***********************************************
* Author: fisty
* Created Time: 2015/3/4 22:15:34
* File Name : UVA10817.cpp
*********************************************** */
#include <iostream>
#include <cstring>
#include <deque>
#include <cmath>
#include <queue>
#include <stack>
#include <list>
#include <map>
#include <set>
#include <string>
#include <vector>
#include <cstdio>
#include <bitset>
#include <algorithm>
using namespace std;
#define Debug(x) cout << #x << " " << x <<endl
#define Memset(x, a) memset(x, a, sizeof(x))
const int INF = 0x3f3f3f3f;
typedef long long LL;
typedef pair<int, int> P;
#define FOR(i, a, b) for(int i = a;i < b; i++)
#define MAX_N 1000
int s, n, m, max_state;
int c[MAX_N];
int cnt[MAX_N]; //统计课程i有几个人教
int sum; //教师的工资总数,因为教师必须都选
int d[1<<8][1<<8];
int p[MAX_N];
int dp(int s1, int s2){
Memset(d, 0x3f);
d[s1][s2] = sum;
for(int i = m+1; i <= m+n; i++){ //m开始代表聘职者
//看每个聘职者可不可以选
for(int _s1 = max_state; _s1 >= 0; _s1--){
for(int _s2 = max_state; _s2 >= 0; _s2--){
if(d[_s1][_s2] >= INF) continue;
int s_1 = (_s1 | p[i]); //选了第i个聘职者之后的s1集合变化
int s_2 = (_s1 & p[i]) | _s2; //选了之后s2的集合变化
d[s_1][s_2] = min(d[s_1][s_2], d[_s1][_s2] + c[i]);
}
}
}
return d[max_state][max_state];
}
int main() {
//freopen("in.cpp", "r", stdin);
while(~scanf("%d%d%d", &s, &m, &n) && s){
max_state = (1 << s) -1;
sum = 0;
int s1 = 0, s2 = 0; //至少有一人教的课程和至少有两人教的课程
Memset(cnt, 0);
FOR(i, 1, m+n+1){
scanf("%d", &c[i]);
char str[MAX_N];
gets(str);
p[i] = 0;
for(int j = 0;j < strlen(str); j++){
if(isdigit(str[j])){
int num = str[j] - '0';
p[i] |= (1 << (num-1));
if(i <= m) ++cnt[num-1];
}
}
if(i <= m){
sum += c[i]; //在职教师必须全部选,所以把他们的薪资全部加起来
s1 |= p[i]; //选了在职教师后s1集合的变化
}
}
FOR(i, 0, s){
if(cnt[i] > 1) s2 |= (1 << i); //s2集合的变化
}
int ans = dp(s1, s2);
printf("%d\n", ans);
}
return 0;
}