这两个题的思想是相似的,可以对比一下。
题目: UVA - 10817 Headmaster’s Headache
题意:
某校有n个教师和m个求职者,已知每人的工资和能教的课程集合,要求支付最少的工资使得每门课都至少有两名教师教学。在职教师必须招聘。
分析:
d[i][s1][s2]表示考虑到第i个教师以后,可以教授的课程集合是s1和s2,还需要花费的钱?其中s1和s2分别保存某门课程是否有一个老师教。因为要求每门课至少两名老师,所以维护两个集合,如果两个集合每门课都有老师教,那么就不需要花钱招聘其他老师了。
维护s1和s2的时候,先填s1再填s2
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
const int INF = 1e9 + 9;
const int N = (1 << 8) + 9;
int d[130][N][N], s[130], w[130];
bool vis[130][N][N];
int ALL, n, S, m;;
int dp (int i, int s1, int s2) {
if (i == m + n) return s1 == ALL && s2 == ALL ? 0 : INF;
if (vis[i][s1][s2]) return d[i][s1][s2];
vis[i][s1][s2] = 1;
int& ans = d[i][s1][s2];
ans = INF;
if (i >= m) ans = dp (i + 1, s1, s2);
for (int k = 0; k < S ; k++)
if (s[i] & (1 << k) ) {
if (! (s1 & (1 << k) ) ) s1 |= (1 << k);
else if (! (s2 & (1 << k) ) ) s2 |= (1 << k);
}
ans = min (ans, w[i] + dp (i + 1, s1, s2) );
return ans;
}
char ch;
int main() {
//freopen ("f.txt", "r", stdin);
while (~scanf ("%d%d%d", &S, &m, &n) && (S + m + n) ) {
memset (vis, 0, sizeof (vis) );
for (int i = 0; i < m; i++) {
scanf ("%d", &w[i]);
s[i] = 0;
while (ch = getchar() ) {
if (ch == '\n') break;
if (ch >= '1' && ch <= '8') s[i] |= (1 << (ch - '1') );
}
}
for (int i = m; i < n + m; i++) {
scanf ("%d", &w[i]);
s[i] = 0;
while (ch = getchar() ) {
if (ch == '\n') break;
if (ch >= '1' && ch <= '8') s[i] |= (1 << (ch - '1') );
}
}
ALL = (1 << S) - 1;
printf ("%d\n", dp (0, 0, 0) );
}
return 0;
}
/*
SampleInput
2 2 2
10000 1
20000 2
30000 1 2
40000 1 2
0 0 0
SampleOutput
6000
*/
题目: UVALive - 4643 Twenty Questions
题意:
有n个长度为m的二进制串,每个都是不同的。
为了把所有字符串区分开,你可以询问,每次可以问某位上是0还是1。
问最少提问次数,可以把所有字符串区分开来。
分析:
这题自己没做出来QAQ,看了题解瞬间明白了~~
f[s1][s2]: 表示提问的问题是{s1}集合,答案是{s2}时,还需要问几次才可以全部区分开,当问题集合为{s1}时, 如果还不能区分所有答案,那么就需要继续再问一个问题,那么可以推出下一个问题的集合为:
nextQuestions = { s1 | (1<< k), 当s1的k位上为0的时候 }
那么可以得到:
f[s1][s2] = 0, 如果和答案s2相同的个数小于等于1,那么已经可以全部区分开了,还要询问0次
f[s1][s2] = { min(f[nextQuestions][s2], f[nextQuestions][s2^(1<< k)]), 当s1的k位上为0时} //相应答案是0或者是1
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
const int INF = 1e9 + 9;
const int N = (1 << 11) + 9;
int f[N][N], vis[N][N], p[200], n, m;
int dp (int s1, int s2) {
if (vis[s1][s2]) return f[s1][s2];
vis[s1][s2] = 1;
int& ans = f[s1][s2];
int cnt = 0;
for (int i = 0; i < n; i++)
if ( (s1 & p[i]) == s2) cnt++;
if (cnt <= 1) return ans=0;
ans = INF;
for (int i = 0; i < m; i++) {
if ( (s1 & (1 << i) ) ) continue;
ans = min (ans, max (dp (s1 | (1 << i), s2 | (1 << i) ), dp (s1 | (1 << i), s2) ) + 1);
}
return ans;
}
int main() {
//freopen ("f.txt", "r", stdin);
while (~scanf ("%d%d", &m, &n) && (n + m) ) {
memset (vis, 0, sizeof (vis) );
int a;
for (int i = 0; i < n; i++) {
p[i] = 0;
for (int j = 0; j < m; j++) {
scanf ("%1d", &a);
if (a) p[i] |= (1 << j);
}
}
printf ("%d\n", dp (0, 0) );
}
return 0;
}
/*
Sample Input
8 1
11010101
11 4
00111001100
01001101011
01010000011
01100110001
0 0
Sample Output
0
2
*/