题目链接
http://codeforces.com/gym/101512/attachments
题目大意
有m道判断题, 给出n个人的答题情况(01字符串),以及得分c
问你有几种标准答案(如果只有一种,就把这种答案输出),使得该标准答案符合每个人的得分情况
数据范围
1 ≤ n ≤ 12
1 ≤ m ≤ 30
0 ≤ c ≤ m
思路
如果m<=15, 我们直接枚举答案就好了
但是这道题m最大有30,直接枚举复杂度最大到o(2^30),超时妥妥的
这时候我们可以用折半搜索(也叫中途相遇法)将m个问题分成两半,对于两部分分别枚举,那么最大复杂度就缩小成了o(2*2^15)
将枚举的每种答案所有人的情况都记下来, (哈希或者放vector里面)再用map映射, 这样就能在log级别查找对于每一种枚举的答案是否符合实际学生的做题情况了
代码
vector写法
#include<bits/stdc++.h>
using namespace std;
char pc[15][50];
int p[15];
map<vector<int>, int> cnt;
vector<int>tmp, as;
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
int n, m;
scanf("%d%d", &n, &m);
for(int i=0; i<n; ++i)
scanf("%s %d", pc[i], &p[i]);
int m1 = m/2, m2 = m - m/2, ans = 0, mi;
tmp.resize(n);
as.resize(n);
cnt.clear();
for(int i=0; i < (1<<m2); ++i)
{
int tt = 0;
for(int t=0; t<n; ++t)
{
tt = 0;
for(int k=0; k<m2; ++k)
if(((i >> k) & 1) == pc[t][k+m1]-'0') ++tt;
tmp[t] = tt;//这里不要clear然后push_back,直接这样一个一个替换就行了,否则会超时
}
++cnt[tmp];
}
for(int i=0; i < (1<<m1); ++i)
{
int tt = 0;
for(int t=0; t<n; ++t)
{
tt = 0;
for(int k=0; k<m1; ++k)
if(((i >> k) & 1) == pc[t][k]-'0') ++tt;
tmp[t] = tt;
}
for(int t=0; t<n; ++t)
tmp[t] = p[t] - tmp[t];
if(cnt[tmp])
{
ans += cnt[tmp]; //这里是+cnt[tmp],而不是直接++ans,注意理解
if(ans == 1)
{
mi = i;
as = tmp;
}
}
}
if(ans != 1) printf("%d solutions\n", ans);
else
{
for(int k=0; k<m1; ++k)
printf("%d", (mi >> k) & 1);
for(int i=0; i < (1<<m2); ++i)
{
int tt = 0;
for(int t=0; t<n; ++t)
{
tt = 0;
for(int k=0; k<m2; ++k)
if(((i >> k) & 1) == pc[t][k+m1]-'0') ++tt;
tmp[t] = tt;
}
if(tmp == as)
{
for(int k=0; k<m2; ++k)
printf("%d", (i >> k) & 1);
puts("");
break;
}
}
}
}
return 0;
}
哈希写法
- 理论上哈希会更快,如果数据比较多,vector效率就不会高了
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long uLL;
char p[15][50];
int v[15];
map<uLL, int> cnt;
map<uLL, int> ct;
int main()
{
int T;
uLL base = 23, tmp, mt, tot;
scanf("%d", &T);
while(T--)
{
ct.clear();
cnt.clear();
int n, m, mi;
scanf("%d %d", &n, &m);
for(int i=0; i<n; ++i)
scanf("%s %d", p[i], v+i);
int m1 = m/2, m2 = m - m/2, ans = 0;
for(int i=0; i < (1<<m2); ++i)
{
tmp = 1;
for(int t = 0; t < n; ++t)
{
tot = 0;
for(int k=0; k < m2; ++k)
if(((i >> k) & 1) == p[t][k+m1] - '0') ++tot;
tmp = tmp * base + tot;
}
++cnt[tmp];
ct[tmp] = i; //有bug?(因为可能多个答案可以得到相同的tmp,不过这里不影响,因为我们只有一种答案时才输出)
}
for(int i=0; i < (1<<m1); ++i)
{
tmp = 1;
for(int t=0; t < n; ++t)
{
tot = 0;
for(int k=0; k < m1; ++k)
if(((i >> k) & 1) == p[t][k] - '0') ++tot;
tot = v[t] - tot;
tmp = tmp * base + tot;
}
if(cnt[tmp])
{
ans += cnt[tmp];
if(ans == 1)
{
mi = i;
mt = tmp;
}
}
}
if(ans != 1) printf("%d solutions\n", ans);
else
{
for(int k=0; k < m1; ++k)
printf("%d", (mi >> k) & 1);
mi = ct[mt];
for(int k=0; k < m2; ++k)
printf("%d", (mi >> k) & 1);
puts("");
}
}
return 0;
}
- 实际上哈希反而比vector写法慢了500msQAQ, 这特么就尴尬了
可能是因为哈希之后我们没办法从哈希值反过来得到枚举的答案,还要用一个map去维护哈希值和答案