链接:UVA10817
题意:需要讲s个课程,现在有m个老师和n个应聘的,要求没门课最少有两名老师,求出最少支付的工资。
状压DP。。。。。用一个三维数组d[i][s1][s2],i表示第i个老师(or应聘者),s1表示恰好有一个人教的科目的集合,s2表示至少有两个教科目的集合,i表示考虑前i个人的情况。。
然后记忆化搜索,当搜到i为n+m时,如果s2还不是(1<<s)-1,就说明不可能满足题目要求(在这题一定满足的)。。。
转移方程是,d(i,s1,s2)=min(d(i+1,s1',s2')+c[i],d(i+1,s1,s2));...。第一种状态表示聘用i,第二种表示不用,第二种只有当i>=m时才有。
位运算:&表示寻找一个元素是否在这个集合,|表示加进这个集合,^表示去掉。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=125;
const int INF=1<<30;
int m,n,s,st[MAXN],d[MAXN][1<<8][1<<8],c[MAXN];
int dp(int i,int s0,int s1,int s2)
{
if(i==m+n)
return s2==(1<<s)-1?0:INF;
int &ans=d[i][s1][s2];
if(ans>=0)
return ans;
ans=INF;
if(i>=m)
ans=min(ans,dp(i+1,s0,s1,s2));
int m0=st[i]&s0,m1=st[i]&s1;
s0^=m0,s1=(s1^m1)|m0,s2|=m1;
ans=min(ans,c[i]+dp(i+1,s0,s1,s2));
return ans;
}
int main()
{
int i;
while(scanf("%d%d%d",&s,&m,&n)!=EOF&&s)
{
int x;
memset(d,-1,sizeof(d));
memset(st,0,sizeof(st));
for(i=0;i<m;i++)
{
scanf("%d",&c[i]);
while(1)
{
char ch;
scanf("%d",&x);
st[i]|=(1<<(x-1));
ch=getchar();
if(ch=='\n')
break;
}
}
for(i=m;i<m+n;i++)
{
scanf("%d",&c[i]);
while(1)
{
char ch;
scanf("%d",&x);
st[i]|=(1<<(x-1));
ch=getchar();
if(ch=='\n')
break;
}
}
int ans=dp(0,(1<<s)-1,0,0);
printf("%d\n",ans);
}
return 0;
}