UVA 10817 Headmaster's Headache(dp 状态压缩 01背包)

题目大意:
春田花花小学的校长正在考虑聘用一些新教师来教授某些科目,有许多的教师提交简历。每个教师能教授一个或者更多的科目,校长想选择这些应聘者来教书,确保让每个科目可以都有两个老师可以教书,并且然总花费最小。

输入:
s,m,n,s代表科目数量,m代表现职教师的人数,n代表应聘者的人数。
接下来m行输入现职教师的工资和可以教的科目,n行输入应聘者的工资和可以交的科目。
你必须全部雇佣现职教师,应聘者可以选择性雇佣,现在给你这些教师的工资,和这些教师可以教的科目,要求总花费最小。

输出
最小的总花费

思路:
dp[s1][s2]: s1表示课程集合{ s1 }都至少有一个教师教的情况。
s2表示课程集合{ s2 }都至少有两个教师教的情况。

p[i]表示每个求职者可以教的科目的状态压缩。
每个求职者的p[i],对于每个求职者,要么选,要么不选,就是01背包问题。
对于s1,s2,可以根据当前枚举到的求职者课程即可,可推出下一个状态:
nextS1 = p[i] | s1,
nextS2 = (p[i] & s1) | s2
dp[nextS1][nextS2] = min(dp[nextS1][nextS2], dp[s1][s2] + p[i])

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 105;
int s, m, n;
int maxSt;
int minCost;
int vis[9]; //标记现职教师教的课程
int p[N], cost[N];
int dp[1 << 8][1 << 8];
//dp[s1][s2]: s1表示课程集合{ s1 }都至少有一个教师教的情况。s2表示课程集合{ s2 }都至少有两个教师教的情况。
void init() {
    maxSt = (1 << s) - 1;
    minCost = 0;
    memset(vis,0,sizeof(vis));
    memset(p,0,sizeof(p));
}
int solve(int st1, int st2) {
    memset(dp, INF, sizeof(dp));
    dp[st1][st2] = minCost;
    for(int i = m+1; i <= m + n; i++) {
        for(int s1 = maxSt; s1 >= 0; s1--) {
            for(int s2 = maxSt; s2 >= 0; s2--) {
                if(dp[s1][s2] != INF) {
                    int nextSt1 = (p[i] | s1);
                    int nextSt2 = (p[i] & s1) | s2;
                    dp[nextSt1][nextSt2] = min(dp[nextSt1][nextSt2], dp[s1][s2] + cost[i]);
                }
            }
        }
    }
    return dp[maxSt][maxSt];
}
void read(int &st1, int &st2) {
    string str;
    int x;
    for(int i = 1; i <= m + n; i++) {
        getline(cin, str);
        stringstream ss(str);
        ss >> cost[i];
        while(ss >> x) {
            p[i] |= (1 << (x-1));
            if(i <= m) {
                vis[x]++;
            }
        }
        if(i <= m) {
            minCost += cost[i];
            st1 |= p[i];
        }
    }
    for(int i = 1; i <= s; i++) {
        if(vis[i] > 1) {
            st2 |= (1 << (i-1));
        }
    }
}
int main() {
    while(cin >> s >> m >> n) {
        getchar();
        if(!(s + m + n)) break;
        init();
        int st1 = 0, st2 = 0;
        read(st1, st2);
        cout << solve(st1, st2) << endl;
    }
    return 0;
}

你可能感兴趣的:(uva,状态压缩,10817)