题意:
n个人做m道题目,给出n个人的答案,已知每个人对了几道,求正确答案有几种情况,如果只有一种,就输出那种方案。(1≤n≤12,1≤m≤30)
思路:
如果直接枚举的话有2^30种情况,这样肯定是不行的,如果m/2的话就可以,所以可以考虑折半搜索。
枚举前半段的正确答案,把每个人后半段正确的数量放在vector里存起来(总正确的-前半段正确的),然后把这种情况放在map里,因为可能要输出答案,所以map的second要放两个东西,一个是这个vector的出现的情况次数和出现的情况是什么样的(之前枚举的正确答案)。
然后对于后半段,枚举后半段的正确答案,每个人的正确题数放在vector之后在map里找,如果有相同的话就ans+=出现这个情况的次数。
最后输出答案就可以了,如果要拼起来的话就拼起来。
这就实现了复杂度的降低,本来要2^30的,现在只要2^15*2。
这里map如何实现查找呢?
可以利用auto,查找的时候用auto的find函数,如果找不到的话返回map.end()(最后一个元素的后面一个元素)。
这里vector还有一个优化,用指定长度的方式,直接用下标访问实现覆盖,会快一点。
下次看到选或不选n/2可以枚举的就可以试试折半搜索这种方式。
代码:
int n;
struct node
{
char ans[35];
int num;
}a[15];
map<vector<int>,pair<int,int> >mp;//个数/答案
int main()
{
int t,m;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
{
scanf("%s",a[i].ans);
scanf("%d",&a[i].num);
}
mp.clear();
vector<int>v(n);
//[0,m/2-1]
int tot=1;
for(int i=0;i<m/2;i++)
tot*=2;
for(int i=0;i<tot;i++)//正确答案
{
int sign=0;
for(int j=0;j<n&&sign==0;j++)
{
int cnt=0;//正确的题数
for(int k=0;k<m/2;k++)
{
int temp=(i>>k)&1;
if(temp==a[j].ans[k]-'0')cnt++;
}
if(cnt<=a[j].num)v[j]=a[j].num-cnt;
else sign++;
}
if(sign==0)
{
mp[v].first++;
mp[v].second=i;
/*for(int j=0;j<m/2;j++)
printf("%d",i>>j&1);
printf(" ");
for(int j=0;j<v.size();j++)
printf("%d ",v[j]);
printf("\n");*/
}
}
//[m/2,m-1]
int ccnt=0,ans1=0,ans2=0;
tot=1;
for(int i=0;i<m-m/2;i++)//!!!注意是m-m/2
tot*=2;
for(int i=0;i<tot;i++)
{
int sign=0;
for(int j=0;j<n&&sign==0;j++)
{
int cnt=0;
for(int k=m/2;k<=m-1;k++)
{
int temp=(i>>(k-m/2))&1;
if(temp==a[j].ans[k]-'0')cnt++;
}
if(cnt<=a[j].num)v[j]=cnt;
else sign++;
}
if(sign==0)
{
auto it=mp.find(v);
if(it!=mp.end())
{
ccnt+=it->second.first;
ans1=it->second.second;
ans2=i;
}
}
}
if(ccnt!=1)printf("%d solutions\n",ccnt);
else
{
for(int i=0;i<m/2;i++)
printf("%d",ans1>>i&1);
for(int i=0;i<m-m/2;i++)
printf("%d",ans2>>i&1);
printf("\n");
}
}
return 0;
}
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!