题意:

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 协议 ,转载请注明出处!

BAPC2014FinalA 按时间拆点+最大流 Previous
BAPC2014FinalE 线段树 Next