【算法竞赛入门经典】集合的动态规划;位运算 例题9-15 UVa10817
例题UVa1210817
The headmaster of Spring Field School is considering employing some new teachers for certain subjects. There are a number of teachers applying for the posts. Each teacher is able to teach one or more subjects. The headmaster wants to select applicants so that each subject is taught by at least two teachers, and the overall cost is minimized.
Input
The input consists of several test cases.
The format of each of them is explained below:
The first line contains three positive integers S, M and N. S (≤ 8) is the number of subjects, M( ≤ 20) is the number of serving teachers, and N (≤ 100) is the number of applicants.
Each of the following M lines describes a serving teacher. It first gives the cost of employing him/her (10000 ≤ C ≤ 50000), followed by a list of subjects that he/she can teach. The subjects are numbered from 1 to S. You must keep on employing all of them. After that there are N lines, giving the details of the applicants in the same format.
Input is terminated by a null case where S = 0. This case should not be processed.
Output
For each test case, give the minimum cost to employ the teachers under the constraints.
Sample Input
2 2 2
10000 1
20000 2
30000 1 2
40000 1 2
0 0 0
Sample Output
60000
分析
平心而论,这道题的状态转移不是非常友好,理解起来很不舒服。
大概有点类似于背包问题?第i个老师选不选的问题,但是状态转移不是一下就能做好的东西。
方便起见,用s1,s2表示有一个人教授的科目和两个人及以上的人教授的科目。
以前固定思维的是,d[i]表示的是前i的花费,然后d[i]根据以前刷表填好的数据来填,但是这题目不能这么做,原因:
那我们假设d[i][s1][s2]是前i个人考虑完之后,状态为s1,s2时的总耗费,发现计算d[i][s1][s2]难以进行状态转移,因为你不知道d[i][s1][s2]是从d[i-1][X][X]哪种状态变来的。
因此我们回头审视一下这个过程,发现你选择完第i人之后,第i+1个人需要面临的状态就确定了,因此,不妨从前往后计算。那么换一种表示方式,d[i][s1][s2]就变成了,第i个人面对的状态是s1,s2时,第i个人到最后一个人的总花费。
这样的目的时,确定第i个人的状态之后,能够轻松的确定第i+1个人面对的状态,从而能够轻易的进行状态转移。
这里学到了:状态转移并不一定总是被动的自己去求上一个状态在哪,也有可能是求出状态供给下一个用并转移到下一个那边,这在集合的动态规划里是常见的。
那么,d[i][s1][s2]的转移就成为了:
1.不选择第i个人(只有i>m时才允许不选),此时第i+1个人和第i个人面对的状态相同
ans=dp(i+1,s0,s1,s2);
2.选择第i个人,此时,根据计算可得到新的s0,s1,s2即第i+1个人面对的状况。
把这个状态跟INF或者不选择第i个人时的值比较,求小的那个
int m0=able[i]&s0,m1=able[i]&s1;
s0^=m0;
s1=(s1^m1)|m0;
s2|=m1;
ans=min(ans,dp(i+1,s0,s1,s2)+cost[i]);
样例实现代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<sstream>
#define maxs 9
#define maxn 120+5
#define INF 1000000000
using namespace std;
int s,m,n;
int cost[maxn],able[maxn],d[maxn][1<<maxs][1<<maxs];
int dp(int i,int s0,int s1,int s2){
if(i==m+n+1){
if(s2==(1<<s)-1)
return 0;
else
return INF;
}
int &ans=d[i][s1][s2];
if(ans>=0)
return ans;
ans=INF;
if(i>m){
ans=dp(i+1,s0,s1,s2);
}
int m0=able[i]&s0,m1=able[i]&s1;
s0^=m0;
s1=(s1^m1)|m0;
s2|=m1;
ans=min(ans,dp(i+1,s0,s1,s2)+cost[i]);
return ans;
}
int main(){
int temp;
while(cin>>s>>m>>n&&s){
getchar();
memset(able,0,sizeof(able));
string st;
for(int i=1;i<=m+n;i++){
getline(cin,st);
stringstream ss(st);
ss>>cost[i];
while(ss>>temp){
able[i]|=(1<<(temp-1));
}
}
memset(d,-1,sizeof(d));
int ans=dp(1,(1<<s)-1,0,0);
cout<<ans<<endl;
}
return 0;
}