uva10817

题目大意:
这间学校开设S门课,给出校长已经有的师资n,然后再给出m个应聘者,每门课至少有两名任课老师,求最少需要的雇佣工资。

思路:
01背包问题,要嘛选这名老师教这门课要嘛不选。
将状态压缩。
dp[i][k]表示第i种状态第k个应聘者时可以有的最低雇佣工资。
假设有4门功课,每门功课有一个任课老师,表示为00001111,如果第一门有两个任课老师表示为00011110。有两个以上任课老师的就将i + s的位置至为1,i的位置变成0
代码:

#include 
using namespace std;
#include 
#include 

const int INF = 0x3f3f3f3f;
const int N = 105;
const int M = 8;
const int maxn = 1 << (2 * M);

int s,m,n;
int v[M + 1];
int cost[N];
int dp[maxn][N];
int num[N];
int teach[N][M];
int _max;

int DP(int st,int k) {
    int &ans = dp[st][k];

    if(ans != INF)
        return ans;
    if(k <= n) {

        int newst = st;
        for(int j = 0; j < num[k]; j++) {
            if(newst & (1 << (teach[k][j] - 1)))  { //表示teach[k][j]这门课目前有一个老师在教
                newst |= (1 << (teach[k][j] - 1 + s));//将i + s的位置置为1
                newst &= ~(1 << (teach[k][j] - 1));//将i的位置置为0
            }
            else if(!(newst &(1 << (teach[k][j] - 1 + s))))//如果i的位置为0 i + s的位置也为0的话 将i位置置为1
                newst |= (1 << (teach[k][j] - 1));
        }
        ans = min(ans,min(DP(newst, k + 1) + cost[k],DP(st,k + 1)));
    }
    if(ans == INF)
        ans = INF + 1;
    return ans;
}
int main() {

    int st;
    int money,t;
    int sum;
    char c;
    while(scanf("%d %d %d",&s,&m,&n) && (s || m || n)) {
        st = sum = 0;
        memset(v,0,sizeof(v));
        for(int i = 1; i <= m; i++) {
            scanf("%d",&money);
            sum += money;
            while(scanf("%c",&c)) {
                if(c == '\n')
                    break;
                if(c >= '1' && c <= '8')
                    if(v[c - '0'] < 2)
                        v[c - '0'] ++;//保存目前每门课有几个任课老师
            }
        }
    //  cout << "1";
        for(int i = 1; i <= n; i++) {
            scanf("%d",&cost[i]);
            t = 0;
            while(scanf("%c",&c)) {
                if(c == '\n')
                    break;
                if(c >= '1' && c <= '8') {
                    teach[i][t++] = c - '0';//保存第i个应聘者可以教的课程的编号
                }
            }
            num[i] = t;//第i个应聘者总共可以较几门课
        }
        for(int i = 1; i <= s; i++)
            if(v[i])//如果v[i] > 1的话那么表示该门课有两个以上任课老师 必须用s * (v[i] - 1) v[i]最大为2,不能超过2
                st += 1 << (i - 1 + s * (v[i] - 1));
        _max = (1 << (2 * s)) - (1 << s);//最大的状态
        for(int i = st; i < _max ;i++) 
            for(int j = 1; j <= n + 1; j++)
                dp[i][j] = INF;
        for(int i = 1; i <= n + 1; i++)
            dp[_max][i] = 0;
        printf("%d\n",DP(st,1) + sum);
    }
    return 0;
}

你可能感兴趣的:(DP,状态压缩,01背包)