Headmaster's Headache

思路

这里参考了一位大佬的blog,我这里只是简单说一下我的理解

  • 首先这道题学科数量很少,所以可以用状态压缩做这道题,但是这里学科要两个老师教,所以我们把一个学科分为2个相邻位,比如把原本第一个学科1,分为第1位和第2位,这样只要所有位都为0的时候,就表明所有学科都教完了
  • 接下来就是把那些已经在学校,必须用的老师给用了,统计下所有花费,更新一下状态,这道题的输入有点不舒服。
  • 再把那些应聘的状态给读入,这里只存下同一学科中低的一位,在后面更新状态的时候考虑两个为就可以了
  • dfs顺序枚举,记录下当前点和状态,记忆化搜索和简单剪枝就行
  • 状态方程:f[n][st]=min(f[n-1][st],f[n-1][change(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) {//所教学科状态有一个不为0,就减去该位
				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;//定义s*2个位来存下所有状态
		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);//st1和st2分别对应同一个学科的两个位
				int st2 = 1 << (temp * 2 - 2);
				if (st1 & st) {//如果有一个不为0,就减去该位
					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;
}

你可能感兴趣的:(动态规划)