题意:一个学校要招聘老师,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
#include
#include
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;
}