在这里插入图片描述
UVA10817
定义s0s_0为没人教的科目的集合,s1s_1为恰好有一人教的科目的集合,s2s_2为至少有两人教的科目的集合。则定义dp[i][s1][s2]dp[i][s_1][s_2]为考虑前i名教师时科目情况为相应科目情况为s1,s2s_1,s_2时的最小花费。AllSetAllSet为全集。这里的集合采用二进制表示法。ii的编号从0M+N10\sim M+N-1,即0M10\sim M-1为在职教师,MM+N1M\sim M+N-1为应聘教师。
初始化
dp=1dp=-1表示未计算过
转移方程

  1. 枚举应聘者时
    dp[i][s1][s2]=min(dp[i+1][s1][s2]idp[i+1][s1][s2]+teachers[i].Costi)dp[i][s_1][s_2]=min(\\ dp[i+1][s_1][s_2]不聘用第i名教师,\\ dp[i+1][s_1'][s_2']+teachers[i].Cost聘用第i名教师\\)
    其中,s1,s2s_1',s_2'为聘用此人后的新s1,s2s_1,s_2
  2. 枚举在职教师时,只能聘用
    dp[i][s1][s2]=dp[i+1][s1][s2]+teachers[i].Costdp[i][s_1][s_2]=dp[i+1][s_1'][s_2']+teachers[i].Cost

边界条件
i==M+Ni==M+N时,即dp[M+N]dp[M+N]要赋值给dp[M+N1]dp[M+N-1],此时为考虑所有教师后,因此若此时s2==AllSets_2==AllSet,则说明此时所有课都由至少两名老师,无需再额外聘用老师。否则返回infinf,以便做minmin运算。注意:实现中由于递归(先进后出)的性质,实际上枚举顺序是从M+N1M+N-1到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;
}
posted on 2020-01-20 01:27  SCU_GoodGuy  阅读(174)  评论(0)    收藏  举报