UVA10817 校长的烦恼 Headmaster’s Headache(记忆化搜索+状压dp)
题意:
题目描述 斯普林菲尔德(春之田野???)学院的校长正在考虑为某些科目招聘新老师 现在有一批申请岗位的老师 每个老师能教一个或多个科目 校长想要选择一些申请者以保证每个科目都至少有两名老师能教 同时总共花的钱要最小
输入格式: 输入由多组测试组成,它们的格式如下: 第一行包含三个给定的整数S,M和N, S (<=8)是科目的总数 M(<=20)是在职教师数,N(<=100)是应聘者的数量
接下来M行每行描述一个在职教师 首先给出雇佣他的花费C (10000<=C<=50000) ,接下来是他能教的科目列表 科目用1—S的整数表示 你必须保证继续聘用他们
之后N行,以同样格式给出应聘者的信息
输入以一个空测试S=0结束,你不应当处理它 (这句有些别扭,不过懂意思就好)
思路:
科目的总数只有$8$,考虑把每个老师会哪几门课这个条件转化为二进制数表示。
然后记忆化搜索:$dp[i][s0][s1][s2]$表示枚举到第$i$个人时,没有人教的课程的二进制数表示为$s0$,有一个人教的课程表示为$s1$,有两个人教的课程表示为$s2$.
当枚举到第$i$个人并且$i>=m$时(所有的下标从$0$开始),可以不选择这个人。
对于所有的教师都可以选择这个人,考虑这个人被选后会对记忆化搜索的变量产生哪些影响。
1.记录别人都不会但是第$i$个人会的课程
2.记录别人有一个人会而且第$i$个人也会的课程。
3.
s0=s0^m0;///在没人会的集合里面去除i会的 s1=(s1^m1)|m0;///首先在一个人会的集合里去除i也会的,然后加上只有i会的,这是所有一个人会的 s2=s2|m1;///现在相当于两个人会
代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<ll, ll>PLL; typedef pair<int, int>PII; typedef pair<double, double>PDD; #define I_int ll inline ll read() { ll x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-')f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } #define read read() #define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0) #define multiCase int T;cin>>T;for(int t=1;t<=T;t++) #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define repp(i,a,b) for(int i=(a);i<(b);i++) #define per(i,a,b) for(int i=(a);i>=(b);i--) #define perr(i,a,b) for(int i=(a);i>(b);i--) ll ksm(ll a, ll b, ll p) { ll res = 1; while(b) { if(b & 1)res = res * a % p; a = a * a % p; b >>= 1; } return res; } const int inf = 0x3f3f3f3f; #define PI acos(-1) const int maxn=1000000000; int s,n,m,val[150],st[150]; int dp[150][1<<8][1<<8]; int dfs(int i,int s0,int s1,int s2){ if(i==n+m){ return s2 == (1 << s) - 1 ? 0 : inf; } int &res=dp[i][s1][s2]; if(res>=0) return res; res=inf; if(i>=m){ res=dfs(i+1,s0,s1,s2); } int m0=st[i]&s0;///别人都不会 i会 int m1=st[i]&s1;///只有一个人会 i会 s0=s0^m0;///在没人会的集合里面去除i会的 s1=(s1^m1)|m0;///首先在一个人会的集合里去除i也会的,然后加上只有i会的,这是所有一个人会的 s2=s2|m1;///现在相当于两个人会 res=min(res,dfs(i+1,s0,s1,s2)+val[i]); return res; } int main() { string str; while(getline(cin,str)){ stringstream ss; ss<<str; ss>>s>>m>>n; if(!s) break; rep(i,0,m+n-1){ getline(cin,str); stringstream ss; ss<<str; ss>>val[i];///工资 st[i]=0; int x; while(ss>>x){ x--; st[i]|=(1<<x); } } memset(dp,-1,sizeof dp); printf("%d\n",dfs(0,(1<<s)-1,0,0)); } return 0; } /* **/