1. 程式人生 > >Headmaster's Headache UVA - 10817 (01揹包狀壓dp)

Headmaster's Headache UVA - 10817 (01揹包狀壓dp)

 

The headmaster of Spring Field School is consider- ing employing some new teachers for certain subjects. There are a number of teach- ers applying for the posts. Each teacher is able to teach one or more sub jects. The headmaster wants to select applicants so that each sub- ject is taught by at least two teachers, and the overall cost is minimized.

Input

The input consists of several test cases. The format of each of them is explained below:

The first line contains three positive integers S, M and N. S (≤ 8) is the number of subjects, M(≤ 20) is the number of serving teachers, and N (≤ 100) is the number of applicants.

Each of the following M lines describes a serving teacher. It first gives the cost of employing him/her (10000 ≤ C ≤ 50000), followed by a list of subjects that he/she can teach. The subjects are numbered from 1 to S. You must keep on employing all of them. After that there are N lines, giving the details of the applicants in the same format.

Input is terminated by a null case where S = 0. This case should not be processed.Output

For each test case, give the minimum cost to employ the teachers under the constraints.

Sample Input

222 10000 1 20000 2 30000 1 2 40000 1 2 000

Sample Output

60000

題意:有s個學科,現在在學校有n個教師在教書,這些教師必須要被僱傭,現在還有m個教師正在應聘。現在給出這n個在職教師的工資和能教的科目,給出m個應聘教師的工資和能教的科目,現在希望這s個科目,每個都有至少兩個教師教授,問你最少需要支付的工資是多少。(不能解僱原來的老師)

題解:簡單狀壓的01揹包問題,看程式碼和註釋

//#include"bits/stdc++.h"
//#include<unordered_map>
//#include<unordered_set>
#include<iostream>
#include<sstream>
#include<iterator>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<vector>
#include<bitset>
#include<climits>
#include<queue>
#include<iomanip>
#include<cmath>
#include<stack>
#include<map>
#include<ctime>
#include<new>
using namespace std;
#define LL long long
#define ULL unsigned long long
#define MT(a,b) memset(a,b,sizeof(a))
#define lson l, mid, node << 1
#define rson mid + 1, r, node << 1 | 1
const int INF  =  0x3f3f3f3f;
const int O    =  1e5;
const int mod  =  1e4 + 7;
const int maxn =  1e2+5;
const double PI  =  acos(-1.0);
const double E   =  2.718281828459;


int s, m, n, sum, sta1, sta2;
int cost[maxn], sub[maxn];
int dp[2][1<<8][1<<8];
// sum為所有老師的費用和,sta1,sta2分別表示所有老師總教學狀態,分別對應下面的s1和s2
// cost[i],sub[i]分別表示第i為應聘者的費用和教學狀態
// s1表示恰有一人教的科目集合,s2表示已經至少兩人教的科目集合
// dp[i][s1][s2]表示前i個應聘者達到狀態s1和s2的最小花費(這裡i只開了2維,可滾動一下陣列,節約記憶體)
// 運用01揹包的思想編碼

void init(){
    sum = 0; sta1 = 0; sta2 = 0;
    MT(cost, 0); MT(sub, 0);
    int fee; char c;
    while(m --) {
        scanf("%d", &fee); sum += fee;
        while((c = getchar()) != '\n' ){
            if(c == ' ') continue;
            int s1 = sta1, s2 = sta2;
            int num = 1 << (c - '1');
            sta1 = ((s2 ^ num) & num) ^ s1;
            sta2 = (s1 & num) | s2;
        }
    }
    for(int i=1; i<=n; i++) {
        scanf("%d", &fee); cost[i] = fee;
        while((c = getchar()) != '\n'){
            if(c == ' ') continue;
            sub[i] ^= 1 << (c - '1');
        }
    }
}

int main(){
    while(scanf("%d%d%d", &s, &m, &n) && s+n+m){
        getchar();
        init(); //讀入資料並計算sta1,sta2和sum
        MT(dp, INF); dp[0][sta1][sta2] = sum;
        for(int i=1; i<=n; i++) {
            for(int s1=0; s1<(1<<s); s1++){
                for(int s2=0; s2<(1<<s); s2++){
                    if(s2 & s1) continue;
                    int ss1 = ((s2 ^ sub[i]) & sub[i]) ^ s1;
                    int ss2 = (s1 & sub[i]) | s2;
                    // ss1, ss2 分別表示在s1,s2狀態下加入第i為應聘者的狀態
                    dp[i&1][ss1][ss2] = min(dp[!(i&1)][s1][s2]+cost[i], dp[i&1][ss1][ss2]);
                    dp[i&1][ss1][ss2] = min(dp[!(i&1)][ss1][ss2], dp[i&1][ss1][ss2]);
                    dp[i&1][s1][s2] = min(dp[i&1][s1][s2], dp[!(i&1)][s1][s2]);
                }
            }
        }
        printf("%d\n", dp[n&1][0][(1<<s)-1]);
    }
    return 0;
}

附幾組資料


2 2 2
10000 1
20000 2
30000 1 2
40000 1 2

3 2 2
100 1 2
100
100 3
100 1 2 3

4 3 3
1 1 2 3
10 1
10 2
1000 3
100 3 4
100 4

2 2 2
100 1 2
100 1
200
300 2

3 2 2
100 1 2
100 1
1000 2 3
100 1 2 3

3 3 3
1 1 2
1 1 2
1 1 2
100 1 3
1 3
2 1 2 3

4 4 4
213 1 2 3
2313 1 2 4
3213 1 2
3213 1
123 1 2 3
3214 1 2 4
3123 1 2 4
313 1 2 3

答案:
60000
400
221
500
1300
6
12198