题目大意:
这间学校开设S门课,给出校长已经有的师资n,然后再给出m个应聘者,每门课至少有两名任课老师,求最少需要的雇佣工资。
思路:
01背包问题,要嘛选这名老师教这门课要嘛不选。
将状态压缩。
dp[i][k]表示第i种状态第k个应聘者时可以有的最低雇佣工资。
假设有4门功课,每门功课有一个任课老师,表示为00001111,如果第一门有两个任课老师表示为00011110。有两个以上任课老师的就将i + s的位置至为1,i的位置变成0
代码:
#include <iostream>
using namespace std;
#include <stdio.h>
#include <cstring>
const int INF = 0x3f3f3f3f;
const int N = 105;
const int M = 8;
const int maxn = 1 << (2 * M);
int s,m,n;
int v[M + 1];
int cost[N];
int dp[maxn][N];
int num[N];
int teach[N][M];
int _max;
int DP(int st,int k) {
int &ans = dp[st][k];
if(ans != INF)
return ans;
if(k <= n) {
int newst = st;
for(int j = 0; j < num[k]; j++) {
if(newst & (1 << (teach[k][j] - 1))) { //表示teach[k][j]这门课目前有一个老师在教
newst |= (1 << (teach[k][j] - 1 + s));//将i + s的位置置为1
newst &= ~(1 << (teach[k][j] - 1));//将i的位置置为0
}
else if(!(newst &(1 << (teach[k][j] - 1 + s))))//如果i的位置为0 i + s的位置也为0的话 将i位置置为1
newst |= (1 << (teach[k][j] - 1));
}
ans = min(ans,min(DP(newst, k + 1) + cost[k],DP(st,k + 1)));
}
if(ans == INF)
ans = INF + 1;
return ans;
}
int main() {
int st;
int money,t;
int sum;
char c;
while(scanf("%d %d %d",&s,&m,&n) && (s || m || n)) {
st = sum = 0;
memset(v,0,sizeof(v));
for(int i = 1; i <= m; i++) {
scanf("%d",&money);
sum += money;
while(scanf("%c",&c)) {
if(c == '\n')
break;
if(c >= '1' && c <= '8')
if(v[c - '0'] < 2)
v[c - '0'] ++;//保存目前每门课有几个任课老师
}
}
// cout << "1";
for(int i = 1; i <= n; i++) {
scanf("%d",&cost[i]);
t = 0;
while(scanf("%c",&c)) {
if(c == '\n')
break;
if(c >= '1' && c <= '8') {
teach[i][t++] = c - '0';//保存第i个应聘者可以教的课程的编号
}
}
num[i] = t;//第i个应聘者总共可以较几门课
}
for(int i = 1; i <= s; i++)
if(v[i])//如果v[i] > 1的话那么表示该门课有两个以上任课老师 必须用s * (v[i] - 1) v[i]最大为2,不能超过2
st += 1 << (i - 1 + s * (v[i] - 1));
_max = (1 << (2 * s)) - (1 << s);//最大的状态
for(int i = st; i < _max ;i++)
for(int j = 1; j <= n + 1; j++)
dp[i][j] = INF;
for(int i = 1; i <= n + 1; i++)
dp[_max][i] = 0;
printf("%d\n",DP(st,1) + sum);
}
return 0;
}