一个需要状态压缩的0-1背包。dp[i][j]中第一维表示每个学科有没有第一个老师教,第二维表示每个学科有没有第二个老师教,这两个信息是要状压的。
对于0-1背包,如果写成dp[i]=max(dp[i],dp[i-w[k]]+c[k])的形式,就要判断i-w[k]是不是大于等于0。在这道题里判断相似的问题有些麻烦,所以写成dp[i+w[k]]=max(dp[i+w[k]],dp[i]+c[k])的形式。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#include <algorithm>
#define INF 10000000
int dp[260][260];
int S,M,N;
int vis[105];
int C[105];
int main(){
while(~scanf("%d%d%d",&S,&M,&N)){
if(!S) break;
int ALL=(1<<S);
memset(dp,0x3f,sizeof(dp));
memset(vis,0,sizeof(vis));
int sum=0;
int ini[10];
memset(ini,0,sizeof(ini));
for(int i=1;i<=M;i++){
int cost,t;
char c;
scanf("%d",&cost);
sum+=cost;
while(1){
scanf("%d%c",&t,&c);
ini[t-1]++;
if(c=='\n') break;
}
}
int u,v;
u=0;v=0;
for(int i=0;i<S;i++){
if(ini[i]==1){
u|=(1<<i);
}
else if(ini[i]>=2){
u|=(1<<i);
v|=(1<<i);
}
}
dp[u][v]=sum;
for(int i=1;i<=N;i++){
scanf("%d",&C[i]);
int t;char c;
while(1){
scanf("%d%c",&t,&c);
t--;
vis[i]|=(1<<t);
if(c=='\n') break;
}
}
for(int i=1;i<=N;i++){
for(int j=ALL-1;j>=0;j--){
for(int k=j;k>=0;k--){
dp[j|vis[i]][k|(j&vis[i])]=min(dp[j|vis[i]][k|(j&vis[i])],dp[j][k]+C[i]);
}
}
}
printf("%d\n",dp[ALL-1][ALL-1]);
}
return 0;
}
/*
3 1 3
10 1
100 1 2 3
20 1 2 3
30 2 3
2 0 2
10 1 2
20 1 2
*/