2014 Benelux Algorithm Programming Contest (BAPC 14) K - Key to Knowledge(折半搜索)

1 篇文章 0 订阅
1 篇文章 0 订阅

题目链接

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去维护哈希值和答案
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值