UVa 10817 - Headmaster's Headache(状压DP)

链接:

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1758

 

题意:

某校有m个教师和n个求职者,需讲授s个课程(1≤s≤8,1≤m≤20,1≤n≤100)。
已知每人的工资c(10000≤c≤50000)和能教的课程集合,要求支付最少的工资使得每门课都至少有两名教师能教。在职教师不能辞退。

 

分析:

用两个集合:s1表示恰好有一个人教的科目集合,s2表示至少有两个人教的科目集合,
而d(i,s1,s2)表示已经考虑了第i个人及其之后所有人时的最小花费。
注意,把所有人一起从0编号,则编号0~m-1是在职教师,m~n+m-1是应聘者。
状态转移方程为d(i,s1,s2) = min{d(i+1, s1', s2')+c[i], d(i+1, s1, s2)},其中第一项表示“聘用”,第二项表示“不聘用”。
当i≥m时状态转移方程才出现第二项。这里s1'和s2'分别表示“招聘第i个人之后s1和s2的新值”,具体计算方法见代码。
下面代码中的can[i]表示第i个人能教的科目集合(注意输入中科目从1开始编号,而代码的其他部分中科目从0开始编号,
因此输入时要转换一下)。下面的代码用到了一个技巧:记忆化搜索中有一个参数s0,表示目前还没有人教的科目集合。
这个参数并不需要记忆(因为有了s1和s2就能算出s0),仅是为了编程的方便(详见s1'和s2'的计算方式)。
最终结果是 dp(0, (1<s)-1, 0, 0),因为初始时所有科目都没有人教。

 

代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <sstream>
 4 using namespace std;
 5 
 6 const int UP = 20 + 100 + 5;
 7 const int INF = 1234567;
 8 int s, m, n, c[UP], can[UP], d[UP][1<<8][1<<8];
 9 
10 int dp(int i, int s0, int s1, int s2){
11     if(i == m + n) return s2 == (1<<s)-1 ? 0 : INF;
12     int& res = d[i][s1][s2];
13     if(res >= 0) return res;
14     res = INF;
15     if(i >= m) res = dp(i+1, s0, s1, s2);
16     int m0 = can[i] & s0, m1 = can[i] & s1;
17     s0 ^= m0;  s1 = (s1 ^ m1) | m0;  s2 |= m1;
18     res = min(res, c[i] + dp(i+1, s0, s1, s2));
19     return res;
20 }
21 
22 int main(){
23     int temp;
24     char str[999];
25     while(~scanf("%d%d%d\n", &s, &m, &n) && s){
26         memset(can, 0, sizeof(can));
27         memset(d, -1, sizeof(d));
28         for(int i = 0; i < m + n; i++){
29             gets(str);
30             stringstream ss(str);
31             ss >> c[i];
32             while(ss){
33                 ss >> temp;
34                 can[i] |= (1 << temp - 1);
35             }
36         }
37         printf("%d\n", dp(0, (1<<s)-1, 0, 0));
38     }
39     return 0;
40 }

 

posted @ 2018-04-08 03:11  Ctfes  阅读(139)  评论(0编辑  收藏  举报