2019牛客多校第九场 D-Knapsack Cryptosystem 【折半查找】

题目来源:https://ac.nowcoder.com/acm/contest/889/D
在这里插入图片描述
在这里插入图片描述
★枯了,爆零场。不过我真的没写过 折半查找 的题 ,我还在那里疯狂dfs剪枝


思路:

这题的数据范围是 36,236已经非常大了,时空都承受不了
但是如果把它砍成两半来处理,每一半只有218 这样的数据就比较好接受了
我们可以用一个 二进制串 表示数据的取舍 比如 010010 就是表示取了第二、五个 ,而这又是独一无二的10进制数(称状态
用一个map映射 状态 和 这个状态所取数的和 的关系 map<LL,int> mp
以及一个 set集合 存储这个和
然后就可以比较好的处理这个问题了
详细见代码~

代码:

#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<deque>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=40;
const int sz=1<<36;
const int inf=2e9;
const int mod=1e9+7;
const double pi=acos(-1);
typedef long long LL;
LL n,m;
LL f[maxn];
bool vis[maxn];
set<LL> s;
map<LL,int> mp;
template<class T>
inline void read(T &x)
{
    char c;x=1;
    while((c=getchar())<'0'||c>'9') if(c=='-') x=-1;
    T res=c-'0';
    while((c=getchar())>='0'&&c<='9') res=res*10+c-'0';
    x*=res;
}
int main()
{
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++) read(f[i]);
    int p=n/2,q=n-p;
    for(int i=0;i<(1<<p);i++){                     //找到前一半的状态和 数据和 的关系 并保存出现过的数据和
        LL sum=0;
        for(int j=0;j<p;j++) if(i&(1<<j)) sum+=f[j+1];
        s.insert(sum);
        mp[sum]=i;
    }                          
    for(int i=0;i<(1<<q);i++){                    //另一半
        LL sum=0;
        for(int j=1;j<=q;j++) if(i&(1<<j-1)) sum+=f[p+j];
        if(s.count(m-sum)){
            for(int j=1;j<=p;j++)
                if(mp[m-sum]&(1<<j-1)) vis[j]=1;
                else vis[j]=0;
            for(int j=1;j<=q;j++)
                if(i&(1<<j-1)) vis[j+p]=1;
                else vis[j+p]=0;
            break;
        }
    }
    for(int i=1;i<=n;i++) cout<<vis[i];
    printf("\n");
    return 0;
}

发布了71 篇原创文章 · 获赞 89 · 访问量 8556

猜你喜欢

转载自blog.csdn.net/weixin_43890662/article/details/99656349