Irrelevant Elements UVA - 1635

Irrelevant Elements

 题目链接:UVA - 1635 

  春田花花幼儿园开始招老师了,要保证每门课都至少有两个老师来交,求最小的花费。

  我们知道,老师120个,你如果用状态压缩必搞不定,2^120(逃

  所以我们只能状态压缩科目,总共就8个科目,2^8,轻轻松松。

  但是至少要两个老师,这这么搞?

  那我们可以设计一个二维数组存储状态咯,第一维代表科目中已经有且只有一个老师教,那第二维代表已经OK的科目有哪些。通过科目全集减去第二维和第一维存在的科目,就得到了当前不存在老师教的科目。

  同样,我们需要求我们对于前i个已经计算过,后m+n-i个还没有考虑时的不同二维状态的最小花费,那么我们还需要一个维度用于存储当前的i的值,故我们有d[i][once][ok]代表满足该状态下的最小花费,其中i代表已经考虑了前i个老师的,后m+n-i个老师还未考虑的情况,once代表对于8门课,只有一个老师教的科目集合,ok代表至少有2个老师教的科目集合,我们最后的求的是当所有老师都没有考虑到的时候,需要花费的最小值,也就是d[0][0][0]的值。

  由于并不是所有状态都要遍历到,为了方便,我们采用递归的方法进行,并每次函数递归的时候多传递一个当前不存在老师教的科目集合。

  

int dp(int i, int remain, int once, int ok) {//一开始d的所有值都为-1,意味着所有情况都未遍历到,i从0开始,teach的i也是从0开始代表编号i(实际上是第i+1个)的老师会的科目集合
    if (i == m + n)//如果达到了最边界(当i==m+n,实际上是第m+n+1个老师,但是最多m+n,所以这是边界情况),对于已经全部考虑了所有老师之后,如果还有没找齐的科目,那么当前状态就肯定是不可达的。
        return ok == (1 << s) - 1 ? 0 : INF;
    int& ans = d[i][once][ok];
    if (ans >= 0)//如果已经遍历过了,直接返回值
        return ans;
    ans = INF;
    if (i >= m)//如果i<m则说明是现在选择的teach[i]是之前已经幼儿园聘用的老师了,老员工了,不能不要,如果大于等于,就意味着前后面的都是来应聘的老师,可要可不要(注意,当i==m,代表选不选第m+1个老师,第x个老师的下标编号都是x-1)。
        ans = dp(i + 1, remain, once, ok);//算出如果不要第i+1(下标编号是i)个老师的最小花费,就直接拿第i+2(编号是i+1)个,因为不要第i+1个老师的,那到还是用上一个第i+2个的值,remain,once,ok这些值不改变,因为如果从第i+2个到第i+1个变化时这些值改变了,则说明你选了第i+1个老师,但是你现在可以不选,因此这三个值不发生变化
//下方计算如果要第i+1个的老师花费
    int t1 = remain & teach[i];//得到第i+1个老师能够使得多少一个老师都没有教的科目变成有一个老师教的科目
    int t2 = once & teach[i];//得到第i+1个老师能够使得多少恰好一个老师教的科目变成已经至少有两个老师教的科目
    remain ^= t1;//删掉已经有一个老师教的科目
    once = (once ^ t2) | t1;//删掉已经至少两个老师教的科目,加入新加的有一个老师教的科目
    ok |= t2;//加入新加的至少有两个老师教的科目
    ans = min(ans, dp(i + 1, remain, once, ok) + cost[i]);
    return ans;
}

 

AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
#define debug
int d[205][1005][1005];
int s, m, n;
int teach[205];
int cost[205];
string read_temp;

int dp(int i, int remain, int once, int ok) {
    if (i == m + n)
        return ok == (1 << s) - 1 ? 0 : INF;
    int& ans = d[i][once][ok];
    if (ans >= 0)
        return ans;
    ans = INF;
    if (i >= m)
        ans = dp(i + 1, remain, once, ok);
    int t1 = remain & teach[i];
    int t2 = once & teach[i];
    remain ^= t1;
    once = (once ^ t2) | t1;
    ok |= t2;
    ans = min(ans, dp(i + 1, remain, once, ok) + cost[i]);
    return ans;
}

int main()
{
#ifdef debug
    freopen(".in", "r", stdin);
    freopen(".out", "w", stdout);
#endif
    while (cin >> s >> m >> n && s + n + m)
    {
        memset(d, -1, sizeof(d));
        memset(teach, 0, sizeof(teach));
        getchar();
        for (int i = 0; i < m + n; i++)
        {
            int temp;
            getline(cin, read_temp);
            stringstream input(read_temp);
            input >> cost[i];
            while (input >> temp)
                teach[i] |= 1 << (temp - 1);//获取第i+1个老师能教学的科目
        }
        printf("%d\n", dp(0, (1 << s) - 1, 0, 0));//最开始所有科目均无人教,所以剩余的是(1<<s) - 1
    }
    return 0;
}

 

posted @ 2020-08-14 15:11  funforever  阅读(95)  评论(0编辑  收藏  举报