题目大意:
有s个学科,现在在学校有n个教师在教书,这些教师必须要被雇佣,现在还有m个教师正在应聘。现在给出这n个在职教师的工资和能教的科目,给出m个应聘教师的工资和能教的科目,现在希望这s个科目,每个都有至少两个教师教授,问你最少需要支付的工资是多少。
解题思路:
动态规划。状压DP。
dp[i]表示i这个状态需要支付的最少工资。因为每个科目至少两个教师,且最多只有8个科目,所以很明显状压DP。
设状态st,st有2*s有效位,低s位表示s个科目有一个教师,高s位也表示s个科目有一个教师。当2*s位都为1的时候表示最少能满足要求的状态。
设状态tt,表示第i个教师能教授科目的状态。
那么状态转移就可以写为:
dp[st | tt] = dp[st] + c[i]
代码:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 105; const int INF = 0x3f3f3f3f; const int maxm = (1 << 16) + 10; int s, m, n; int tot, vis[10], tt[257]; int a[10], c[maxn], dp[maxm], ss[maxn][10]; bool judge(int a, int b) { while (b) { if ((b & 1) && !(a & 1)) return false; b >>= 1; a >>= 1; } return true; } void dfs(int p, int st, int j) { if (p == ss[j][0]) { tt[tot++] = st | (1 << (ss[j][p] - 1)); tt[tot++] = st | (1 << (ss[j][p] - 1 + s)); return; } dfs(p + 1, st | (1 << (ss[j][p] - 1)), j); dfs(p + 1, st | (1 << (ss[j][p] - 1 + s)), j); } int main() { while (~scanf("%d%d%d", &s, &m, &n) && s) { memset(a, 0, sizeof(a)); int tmp, ans = 0; char op; for (int i = 0; i < m; ++i) { scanf("%d", &tmp); ans += tmp; while (scanf("%d%c", &tmp, &op) == 2) { ++a[tmp - 1]; if (op == ' ') break; } } for (int i = 1; i <= n; ++i) { scanf("%d", &c[i]); int tmp, cnt = 0; memset(vis, 0, sizeof(vis)); while (scanf("%d%c", &tmp, &op) == 2) { if (!vis[tmp]) { ss[i][++cnt] = tmp; vis[tmp] = 1; } if (op == ' ') break; } ss[i][0] = cnt; } int be = 0, st = (1 << (2 * s)) - 1; for (int i = 0; i < s; ++i) { if (!a[i]) continue; else if (a[i] == 1) be += (1 << (i + s)); else if (a[i] >= 2) be += (1 << i) + (1 << (s + i)); } memset(dp, 0x3f, sizeof(dp)); dp[be] = 0; for (int j = 1; j <= n; ++j) { tot = 0; dfs(1, 0, j); for (int i = st; i >= be; --i) { if (!judge(i, be)) continue; for (int k = 0; k < tot; ++k) { if (i == (i | tt[k])) continue; dp[i | tt[k]] = min(dp[i | tt[k]], dp[i] + c[j]); } } } printf("%d ", ans + dp[st]); } return 0; }