uva 10817(数位dp)

uva 10817(数位dp)

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

用两个集合,s1表示恰好有一个人教的科目集合,s2表示至少有两个人教的科目集合。设计状态\(d(i, s1, s2)\)表示考虑了后n-i个人时的最小花费。把所有人从1到n+m编号,那么m~n+m-1表示应聘者。因此,状态转移方程为\(d(i, s1, s2)=min\{d(i+1, s1', s2')+c[i], d(i+1, s1, s2)\}\),其中第二项只能在应聘者中使用。s1‘和s2’分别表示招聘第i个人之后s1和s2的新值。具体计算方式见代码。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxs=8, maxn=125, INF=1e9;
int s, m, n, t, c[maxn], st[maxn];
int f[maxn][1<<maxs][1<<maxs];  //[0, 1<<n)正好表示n个元素的所有子集
//c:每个人的工资 st:每个人教授的科目集合
char ch;

int d(int cur, int s0, int s1, int s2){  //si:有i个人教授的科目集合
    if (cur==n) return (s2+1==(1<<s)?0:INF);
    int &ans=f[cur][s1][s2];
    if (ans>=0) return ans; ans=INF;  //必须要>=0!
    if (cur>=m) ans=min(ans, d(cur+1, s0, s1, s2));  //不买这个人
    s2^=s1&st[cur]; s1^=s1&st[cur];
    s1^=s0&st[cur]; s0^=s0&st[cur]; //如果选了这个人
    ans=min(ans, d(cur+1, s0, s1, s2)+c[cur]);
    return ans;
}

int main(){
    while (~scanf("%d%d%d", &s, &m, &n)&&s){
        n=n+m; memset(f, -1, sizeof(f));
        memset(st, 0, sizeof(st));
        for (int i=0; i<n; ++i){
            scanf("%d%c", &c[i], &ch);
            while (ch!='\n'){
                scanf("%d%c", &t, &ch);
                st[i]|=(1<<(t-1));
            }
        }
        printf("%d\n", d(0, (1<<s)-1, 0, 0));
    }
    return 0;
}
posted @ 2018-04-19 08:16  pechpo  阅读(164)  评论(0编辑  收藏  举报