題意:劉汝佳訓練指南p95
題解:此題可以設dp[s2][s1] = 授課狀態爲s2、s1時的最小花費(s1爲課程授課狀態爲1的集合,s2爲授課狀態爲2的集合)
然後視招募的新老師爲要裝進揹包裏的物品,授課狀態s2,s1爲容量,最終答案就爲dp[(1<<S)-1][(1<<S)-1](S爲課程數目)
具體轉移方程如下:
設s2,s1爲招募該老師前的狀態,ns2,ns1爲招募該老師後的狀態則,ns1 = s1 | s ,ns2 = s2 | (s1 & s) ,(s爲該老師能教授的課程)
dp[ns2][ns1] = min(dp[s1 | s[i]][s2 | (s1 & s[i])]) + v[i],s[i]是第i個老師授課集合,v[i]是第i個老師的收費
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<cmath>
#include<stdlib.h>
#include <string.h>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<time.h>
using namespace std;
#define MAX_N 505
#define inf 0x3f3f3f3f
#define LL long long
#define ull unsigned long long
const LL INF = 1e18;
const int mod = 1e8+7;
typedef pair<double, int>P;
int S, M, N;
int tC[25];
int aC[105];
int teacher[25];
int applicant[105];
int dp[1<<8][1<<8];
int getT()
{
char c;
int ans = 0;
int d;
c = getchar();
while(c!='\n') {
scanf("%d", &d);
d--;
ans |= (1<<d);
c = getchar();
}
return ans;
}
void init()
{
memset(dp, inf, sizeof(dp));
}
int main()
{
while(cin >> S >> M >> N && S+M+N) {
init();
int s1 = 0;
int s2 = 0;
int sum = 0;
for(int i=0; i<M; i++) {
scanf("%d", &tC[i]);
teacher[i] = getT();
sum += tC[i];
s2 |= (s1 & teacher[i]);
s1 |= teacher[i];
}
for(int i=0; i<N; i++) {
scanf("%d", &aC[i]);
applicant[i] = getT();
}
dp[s2][s1] = sum;
for(int k=0; k<N; k++) {
int s = applicant[k];
for(int i=(1<<S)-1; i>=0; i--) {
for(int j=(1<<S)-1; j>=0; j--) {
int ns1 = j|s;
int ns2 = i|(j&s);
dp[ns2][ns1] = min(dp[ns2][ns1], dp[i][j]+aC[k]);
}
}
}
/*
for(int i=0; i<(1<<S); i++) {
for(int j=0; j<(1<<S); j++) {
if(dp[i][j] == inf)
printf("0 ");
else
printf("%d ",dp[i][j]);
}
puts("");
}*/
cout << dp[(1<<S)-1][(1<<S)-1] << endl;
}
return 0;
}
/*
8 4 3
1000 1
1000 2
1000 3
1000 2
50000 1 5 7 3 4
50000 1 6 8
50000 1 2 3 4 5 6 7 8
*/