题意:这间学校开设S门课,给出校长已经有的师资n,然后再给出m个应聘者,每门课至少有两名任课老师,求最少需要的雇佣工资。
分析:一开始没什么头绪,看到网上说01背包,一想是那么回事。还有一个技巧是把2*s位的二进制数表示状态,假设有两门课,那么1100表示两门课都有两个以上老师,0011表示这两门课都只有一个老师。根据现有老师算出初始状态,然后转移,转移终点是到达1100的状态(假设有两门课)。个人感觉记忆化搜索比循环转移好用多了,清晰明了。
因为st数组没初始化,卡了3个小时。。。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
#include <vector>
#include <queue>
#define FRER() freopen("in.txt","r",stdin)
#define FREW() freopen("out.txt","w",stdout)
#define go int T;cin>>T;while(T--)
#define debug cout<<"****************"<<endl
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int maxn = 120 + 7,inf =0x3f3f3f3f;
int cost[maxn],st[maxn],dp[maxn][1<<16],cnt[10];
int n,m,s,t;
int chageST(int s1,int s2){
for(int i=0;i<s;i++){
if((s2&(1<<i))){
if((s1&(1<<(s+i)))){
continue;
}else{
if((s1&(1<<i))){
s1-=(1<<i);
s1+=(1<<(i+s));
}else{
s1+=(1<<i);
}
}
}
}
return s1;
}
int dfs(int i,int nowst){
if(dp[i][nowst]!=-1) return dp[i][nowst];
int& ans = dp[i][nowst];
if(nowst==t) return ans = 0;
if(i==0) return ans = inf;
ans = inf;
int ans1 = dfs(i-1, nowst);
int ans2 = dfs(i-1, chageST(nowst, st[i]))+cost[i];
return ans = min(ans,min(ans1,ans2));
}
int main(){
//FRER();
//FREW();
while(~scanf("%d%d%d",&s,&m,&n)&&(n||m||s)){
memset(dp, -1, sizeof(dp));
memset(cnt, 0, sizeof(cnt));
memset(st, 0, sizeof(st));
int sum = 0;
for(int i=0;i<m;i++){
int c;char ch;
scanf("%d",&c);
sum+=c;
while(1){
scanf("%d%c",&c,&ch);
//cout<<c<<endl;
c--;
cnt[c]++;
if(ch=='\n') break;
}
}
for(int i=1;i<=n;i++){
int c;char ch;
scanf("%d",&cost[i]);
while(1){
scanf("%d%c",&c,&ch);
// cout<<c<<endl;
c--;
st[i] += (1<<c);
if(ch=='\n') break;
}
}
int now = 0;
for(int i=0;i<s;i++){
if(cnt[i]>=2){
now |= 1<<(s+i);
}else if(cnt[i]==1){
now |= 1<<i;
}
}
t =(1<<(2*s))-(1<<s);
if(now==t){
cout<<sum<<endl;
}else{
printf("%d\n",dfs(n,now)+sum);
}
}
}