题目链接:https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1758
设 (dp[i][s1][s2]) 表示前 (i) 个人,被恰好一个人教的课程集合为 (s1),被至少两个人教点的课程集合为 (s2),因为状态的原因,转移只能使用刷表法
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 130;
const int INF = 0x3f3f3f3f;
int s, m, n;
int c[maxn], sub[maxn];
int dp[maxn][1 << 9][1 << 9];
ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }
int main(){
while(scanf("%d%d%d", &s, &m, &n) == 3 && s){
memset(sub, 0, sizeof(sub));
string in;
for(int i = 1 ; i <= m + n ; ++i){
scanf("%d", &c[i]);
getline(cin, in);
int len = in.length();
for(int j = 0 ; j < len ; ++j){
if(in[j] >= '0' && in[j] <= '9'){
sub[i] |= (1 << (in[j] - '1'));
}
}
}
memset(dp, 0x3f, sizeof(dp));
dp[0][0][0] = 0;
for(int i = 0 ; i < m + n ; ++i){
for(int s1 = 0 ; s1 < (1 << s) ; ++s1){
for(int s2 = 0 ; s2 < (1 << s) ; ++s2){
if(s1 & s2) continue;
int s0 = ((1 << s) - 1) ^ (s1 | s2);
// 不选
if(i+1 > m) dp[i+1][s1][s2] = min(dp[i+1][s1][s2], dp[i][s1][s2]);
// 选
int ns2 = s2 | (s1 & sub[i+1]);
int ns1 = (s1^(s1 & sub[i+1])) | (s0 & sub[i+1]);
dp[i+1][ns1][ns2] = min(dp[i+1][ns1][ns2], dp[i][s1][s2] + c[i+1]);
}
}
}
printf("%d
", dp[m+n][0][(1<<s)-1]);
}
return 0;
}