[UVA 10817]Headmaster's Headache[状压DP]

题目链接: [UVA 10817]Headmaster's Headache[状压DP]

题意分析:

校长需要s门课,每门至少有两名老师来教,所以他现在想要招老师啦。当然,校长手头本来就有m个老师,每个老师都教着一个或多个课程,这些老师是不能解雇的,必须用。然后现在又n个老师来应聘,每个都有价格和他们能教的课程,校长希望花最少的钱达到他的目标,问:最少多少钱呢?

解题思路:

嘛!s <= 8,赤裸裸的暗示(?)。我们可以设置两个集合,s1对应还需要一个老师教的科目,s2对应已经足够老师教的科目,状态设为dp[i][s1][s2]:代表到第i个老师为止,校长最少需要花多少钱达到目标。当i < m 时,我们必须选择,状态只能更新为添加老师,当i >= m时, 就可以更新为选或不选老师两种状态。

个人感受:

第一次写这道题,不会,然后补题,看别人代码。昨天又一次碰到,除了知道是状压外,依稀只记得集合存科目这种事。今天重新做一遍,看了看状态,自己写了转移,感触颇多啊。之前看刘汝佳代码,他设置了三个集合,整个运算符用得极其魔幻(?),当时理解了半天。这次自己写的转移,只用到了其中的两个集合,发现第一个完全可以省略嘛XD,然后两行就完成了转移XD。重做出奇迹啊。。。。

具体代码如下:

#include<iostream>
#include<cstring>
#include<string>
#include<sstream>
using namespace std;
const int INF = 0x7f7f7f7f, MAXN = 131;
int s, m, n, teach[MAXN], cost[MAXN], dp[MAXN][1 << 8][1 << 8];

int DP(int i, int s1, int s2)
{
    if (i == m + n) return s2 == (1<<s) - 1 ? 0 : INF;  //每个科目都至少两个老师了,那么就不需要再花钱了
    int &ret = dp[i][s1][s2];
    if (ret >= 0) return ret;
    ret = INF;
    if (i >= m) ret = DP(i + 1, s1, s2);         //不选
    s2 |= (s1 & teach[i]);                       //老师能教,并且差一个老师,那么一并运算,剩下的就是满足条件的科目
    s1 |= teach[i];                              //或上去,没人教的科目肯定变成差一个人教
    ret = min(ret, cost[i] + DP(i + 1, s1, s2)); //选
    return ret;
}

int main()
{
    while (cin >> s >> m >> n && s)
    {
        cin.get();
        string ss;
        int x;
        for (int i = 0; i < m + n; ++i)
        {
            getline(cin, ss);
            stringstream sss(ss);
            sss >> cost[i];
            teach[i] = 0;
            while(sss >> x)
            {
                teach[i] |= 1 << (x - 1);
            }
        }
        memset(dp, -1, sizeof dp);
        //for (int i = 0; i < m + n; ++i) cout << cost[i] << ':' << teach[i] << endl;
        cout << DP(0, 0, 0) << '\n';
    }
    return 0;
}


你可能感兴趣的:(uva,状压dp)