
UVA10817
定义:为没人教的科目的集合,为恰好有一人教的科目的集合,为至少有两人教的科目的集合。则定义为考虑前i名教师时科目情况为相应科目情况为时的最小花费。为全集。这里的集合采用二进制表示法。的编号从,即为在职教师,为应聘教师。
初始化:
表示未计算过
转移方程:
- 枚举应聘者时
其中,为聘用此人后的新 - 枚举在职教师时,只能聘用
边界条件:
当时,即要赋值给,此时为考虑所有教师后,因此若此时,则说明此时所有课都由至少两名老师,无需再额外聘用老师。否则返回,以便做运算。注意:实现中由于递归(先进后出)的性质,实际上枚举顺序是从到0。
AC代码:
#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#include<map>
using namespace std;
int S, M, N;
constexpr static int inf = 0x3f3f3f3f;
int dp[130][1 << 8][1 << 8];
int AllSet;
struct Employee {
int Cost, CourseSet;
}teachers[130];
bool Input() {
scanf("%d", &S);
if (!S) {
return false;
}
scanf("%d%d", &M, &N);
AllSet = (1 << S) - 1;
for (int i = 0; i < M + N; ++i) {
scanf("%d", &teachers[i].Cost);
teachers[i].CourseSet = 0;
while (getchar() != '\n') {
int Course;
scanf("%d", &Course);
teachers[i].CourseSet |= (1 << Course - 1);
}
}
return true;
}
int DP(int i, int s0, int s1, int s2) {
if (i == M + N) {
return s2 == AllSet ? 0 : inf;
}
int& ans = dp[i][s1][s2];
//ans!=-1,即此时改dp元素已经被枚举到
if (~ans) {
return ans;
}
ans = inf;
//如果枚举的是应聘者,要考虑不聘用他的情况
if (i >= M) {
ans = DP(i + 1, s0, s1, s2);
}
int
//m0=没有老师教的科目集合与当前老师教的科目集合的交集
&&m0 = teachers[i].CourseSet & s0,
//m1=只有一个老师教的科目的集合与当前老师教科目集合的并集
&&m1 = teachers[i].CourseSet & s1;
//相当于s0-m0,即s0应当去掉当前老师能教的科目
s0 ^= m0;
//s1应去掉s1中与当前老师教的科目重合的科目(此时多了一个老师教,这些科目移到s2),并且加上原来没有老师教的但当前老师能教的科目
s1 = (s1 ^ m1) | m0;
//s2加上原来就有一个老师教现在当前老师又能教的科目
s2 |= m1;
ans = min(ans, teachers[i].Cost + DP(i + 1, s0, s1, s2));
return ans;
}
int main() {
while (Input()) {
memset(dp, -1, sizeof(dp));
printf("%d\n", DP(0, AllSet, 0, 0));
}
return 0;
}
浙公网安备 33010602011771号