zoukankan      html  css  js  c++  java
  • Luogu U13059 某种密码

    应该没什么用的题目链接

    题目背景

    关于某种密码有如下描述:某种密码的原文A是由N个数字组成,而密文B是一个长度为N的01数串,原文和密文的关联在于一个钥匙码KEY。若KEY=∑▒〖Ai*Bi〗,则密文就是原文的一组合法密码。

    题目描述

    现在有原文和钥匙码,请编一个程序来帮助他统计到底有多少个符合条件的密文。

    输入输出格式

    输入格式:

    第一行两个数N,KEY,意义同题目描述;

    第二行N个数表示原文A,意义同题目描述。

    输出格式:

    一个数ANS,表示对于原文A和KEY,有多少组可行的密文B。

    输入输出样例

    输入样例#1:
    3 2
    1 1 2
    输出样例#1:
    2
    
    

    说明

    【样例说明】

    密文110,1*1+1*1+0*2=2

    密文001,0*1+0*1+1*2=2

    一共两组可行的密文。

    【数据范围】

    60%数据满足N<=25

    100%数据满足N<=40,-maxlongint<=∑▒Ai<=maxlongint

    题解:

    这道题可以理解为一个01背包问题的变种,n为物品个数,原文为物品的重量,钥匙码为背包容量,求恰好装满背包的方案数。

    如果是一道普通01背包求恰好装满背包的方案数的题,可以用DP求解。具体思路是设f[i][j]为用前i个物品恰好装满容量为j的背包的方案数,转移方程为f[i][j]=f[i-1][j]+f[i-1][j-w[i]]。显然可以滚动数组或一维数组优化,在此不再赘述。

    然而,观察数据范围会发现,此处物品的重量极大,达到了int的范围,因此DP的时空复杂度均无法承受,然而n的范围较小,可以考虑搜索。

    60分做法:

    直接暴搜,O(2n)枚举放入背包的物品集合,将重量相加并与key比较。然而,对于剩余的40%数据,复杂度太大,仍然无法解决。

    100分做法:

    注意到这是一类经典问题,即从一个集合中选取某个子集,使其中所有元素值之和等于某个给定值。可以使用以空间换时间的折半枚举。类似思想也在BSGS中得到应用。

    将物品集合均分成两个交集为空,补集为全集的集合A、B,对集合A暴力枚举其所有子集中元素和并存入哈希表(可重集),再对集合B暴力枚举每个子集的元素和s,同时查找哈希表中值为(key-s)的元素个数并计数。时间复杂度为O(2*2n/2)=O(2n/2),可以接受。

    注意此处用到的哈希表如果使用STL代替的话,不能使用set,要用multiset或map,然而multiset速度极慢不推荐使用,map速度也稍慢,有被卡1~2个点的风险。不过我自己写的哈希表写挂了只能用map

    代码:

     1 #include<bits/stdc++.h>
     2 #define h(x) (x%MOD)
     3 #define LL long long
     4 using namespace std;
     5 const LL maxn=100,maxm=4e6+10,maxk=1e5+10,MOD=1e5+3;
     6 map<int,int>isvis;
     7 LL a[maxn];
     8 LL n,key,mid,tot=0;LL ans=0;
     9 int main()
    10 {
    11     LL i,j,k,l,r,tmp;
    12     cin>>n>>key;
    13     //l=n/2;r=n-l;
    14     mid=(1+n)>>1;l=mid;r=n-l;
    15     for(i=1;i<=n;i++){scanf("%d",&a[i]);}
    16     for(k=0;k<(1<<l);k++)
    17     {
    18         tmp=0;
    19         for(i=0;i<l;i++){if(k&(1<<i)){tmp+=a[i+1];}}
    20         isvis[tmp]++;
    21     }
    22     for(k=0;k<(1<<r);k++)
    23     {
    24         tmp=0;
    25         for(i=0;i<r;i++){if(k&(1<<i)){tmp+=a[l+i+1];}}
    26         ans+=isvis[key-tmp];
    27     }
    28     cout<<ans;
    29     return 0;
    30 }
    View Code

    PS:

    此外,还有一类01背包的变种。这类问题中物品个数与普通01背包类似,物品重量与背包容量极大,但物品单个价值较小,求装入背包的物品价值的最大值。

    此类问题可以用DP求解,只是需要对状态进行些许修改。设f[i][j]为前i个物品价值总量为j时所需的背包容量的最小值,状态转移方程为f[i][j]=min(f[i-1][j],f[i-1][j-c[i]]+w[i])。最后扫描f[n][i],找到最接近给定背包容量的i输出即可。

  • 相关阅读:
    SqlServer执行Insert命令同时判断目标表中是否存在目标数据
    javascript避免dom事件重复触发
    磁盘结构损坏且无法读取
    iframe在iphone中滚动条无效
    Rancher2.x流水线自动化部署
    微服务模块化需要的几个基础功能
    后端程序员的Vue笔记(一)
    自信从何而来
    C#异步案例一则
    Blazor入坑指南
  • 原文地址:https://www.cnblogs.com/XSC637/p/7616172.html
Copyright © 2011-2022 走看看