题意:一个学校要招聘老师,s门课,已应聘了m个老师,且有n个老师来应聘,给出了m个老师的工资和教哪几门课,和n个应聘者的工资和教哪几门课,要求所有科目至少有两个老师教,问最少花费。

题解:用状态压缩表示所有科目是否有老师教,因为每门课最少两个老师,所以状态要用s * 2个位表示,先初始化所有状态为1,然后先输入m的老师信息,将工资和存起来,然后把科目状态更改为0,然后输入n个应聘者的信息,cost[i]存第i个老师的工资,sta[i]存老师教的哪些课的状态。然后开始dp,对于每个应聘者,就是聘请或者不聘请,如果不聘请状态不变,否则科目状态改变并加上工资,f[n][st]表示在n个应聘者中科目状态st需要的最小花费,状态转移方程:f[n][st] = min{f[n][st],min{dp(n - 1,st),dp(n - 1,change_st(sta[n],st)) + cost[n]}}。

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int N = 105;
const int INF = 0x3f3f3f3f;
int s, m, n;
int f[N][1 << 17];
int sta[N], cost[N];

int change_st(int nt, int st) {
	for (int i = 0; i < s * 2; i += 2) {
		if ((1 << i) & nt) {
			if ((1 << i) & st) {
				st -= 1 << i;
				continue;
			}
			if ((1 << (i + 1)) & st) {
				st -= 1 << (i + 1);
				continue;
			}
		}
	}
	return st;
}

int dp(int n, int st) {
	if (st == 0)
		return 0;
	if (n == 0)
		return INF - 1;
	if (f[n][st] < INF)
		return f[n][st];
	return f[n][st] = min(dp(n - 1, st), dp(n - 1, change_st(sta[n], st)) + cost[n]);
}

int main() {
	while (scanf("%d%d%d", &s, &m, &n) && s) {
		memset(f, INF, sizeof(f));
		int sum = 0, temp;
		int st = (1 << (s * 2)) - 1;
		char c;
		for (int i = 0; i < m; i++) {
			scanf("%d%c", &temp, &c);	
			sum += temp;
			while (c != '\n') {
				scanf("%d%c", &temp, &c);
				int st1 = 1 << (temp * 2 - 1);
				int st2 = 1 << (temp * 2 - 2);
				if (st1 & st) {
					st -= st1;
					continue;
				}
				if (st2 & st) {
					st -= st2;
					continue;
				}
			}
		}
		for (int i = 1; i <= n; i++) {
			scanf("%d%c", &temp, &c);
			cost[i] = temp;
			sta[i] = 0;
			while (c != '\n') {
				scanf("%d%c", &temp, &c);
				int stt = 1 << (temp * 2 - 2);
				sta[i] |= stt;
			}
		}
		printf("%d\n", dp(n, st) + sum);
	}
	return 0;
}