还是集合状态压缩dp,有了前面的经验,已经相对好写了。dp(i,j,k)表示考虑了前i个人,有1个人教的科目集合为j,有2个或更多人教的科目集合为k的最少花费。然后就是各种位运算了。。dp数组初始化为了-1,所以代码看上去比较挫。。这题不知道能不能用最小费用最大流写。。
#include <iostream> #include <stdio.h> #include <cmath> #include <algorithm> #include <iomanip> #include <cstdlib> #include <string> #include <memory.h> #include <vector> #include <queue> #include <stack> #include <map> #include <set> #include <ctype.h> #define INF 10000000 #define ll long long #define min3(a,b,c) min(a,min(b,c)) #define max3(a,b,c) max(a,max(b,c)) #define MAXN 100010 using namespace std; int s;//科目 8 int m;//老师 20 int n;//求职者 100 int dp[110][260][260]; int cnt[10]; vector<int> appl[110]; int c[110]; int main(){ while(cin>>s>>m>>n){ if( !(s||m||n))break; memset(dp,-1,sizeof(dp)); memset(cnt,0,sizeof(cnt)); memset(appl,0,sizeof(appl)); char list[64]; int ts=0; for(int i=1;i<=m;i++){ int a,b; char ch; cin>>a;ts+=a; while(scanf("%d%c",&b,&ch)){ cnt[b]++; if(ch=='\n')break; } } // int _1=0,_2=0; for(int i=1;i<=s;i++){ if(cnt[i]==1)_1+=1<<(i-1); if(cnt[i]>1)_2+=1<<(i-1); } dp[0][_1][_2]=0; // for(int i=1;i<=n;i++){ int a,b; char ch; cin>>a; c[i]=a; while(scanf("%d%c",&b,&ch)){ appl[i].push_back(b); if(ch=='\n')break; } } for(int i=1;i<=n;i++){ for(int j=0;j<(1<<s);j++){ for(int k=0;k<(1<<s);k++){ if(dp[i-1][j][k]==-1)continue; if(dp[i][j][k]!=-1)dp[i][j][k]=min(dp[i][j][k],dp[i-1][j][k]); else dp[i][j][k]=dp[i-1][j][k]; int _1=j,_2=k; for(int l=0;l<appl[i].size();l++){ //顺序不能错 int t=1<<(appl[i][l]-1); _2=_2|(_1&t); _1=_1|t; } _1=_1&(~_2); if(dp[i][_1][_2]!=-1) dp[i][_1][_2]=min(dp[i][_1][_2],dp[i-1][j][k]+c[i]); else dp[i][_1][_2]=dp[i-1][j][k]+c[i]; if(dp[i-1][_1][_2]!=-1)dp[i][_1][_2]=min(dp[i][_1][_2],dp[i-1][_1][_2]); } } } cout<<ts+dp[n][0][(1<<s)-1]<<endl; } return 0; }