【寒假每日一题2022】acw1875. 贝茜的报复【DFS/二进制枚举】

Date:2022.01.26
题意:
农夫约翰和奶牛贝茜喜欢在业余时间互相出数学题。
约翰给贝茜出了一道相当难的问题,导致她没能解决。
现在,她希望通过给约翰出一道有挑战性的难题来报复他。
贝茜给了约翰一个表达式 (B+E+S+S+I+E)(G+O+E+S)(M+O+O),其中包含七个变量 B,E,S,I,G,O,M(O 是变量,不是零)。
对于每个变量,她给约翰一个列表,表中包含该变量可采用的最多 20 个整数值。
她要求约翰计算,共有多少种给变量赋值的方法可以使得表达式的计算结果为偶数。
输入格式
第一行包含一个整数 N。
接下来 N 行,每行包含一个变量和该变量的一个可能值。
每个变量至少出现 1 次,最多出现 20 次。
同一变量不会重复列出同一可能值。
输出格式
输出可以使得表达式的计算结果是偶数的给变量赋值的方法总数。
数据范围
7≤N≤140,
所有变量的可能取值范围 [−300,300]
本题答案不会超出int范围。
输入样例:
10
B 2
E 5
S 7
I 10
O 16
M 19
B 3
G 1
I 9
M 2
输出样例:
6
样例解释
共有 6 种可能的赋值方式:
(B,E,S,I,G,O,M) = (2, 5, 7, 10, 1, 16, 19) -> 53,244
= (2, 5, 7, 10, 1, 16, 2 ) -> 35,496
= (2, 5, 7, 9, 1, 16, 2 ) -> 34,510
= (3, 5, 7, 10, 1, 16, 2 ) -> 36,482
= (3, 5, 7, 9, 1, 16, 19) -> 53,244
= (3, 5, 7, 9, 1, 16, 2 ) -> 35,496
注意,(2, 5, 7, 10, 1, 16, 19) 和 (3, 5, 7, 9, 1, 16, 19),虽然计算结果相同,但是赋值方式不同,所以要分别计数。

思路①:搜索,共七个字母,每个字母最多有20种可能,直接搜索复杂度为 2 0 7 20^7 207。考虑优化,观察性质得到表达式结果为偶数,因此考虑三项相乘结果,只有三项全为奇数相乘才为奇数。进一步,我们只需考虑每个数是奇数还是偶数即能求出这三项乘积中每一项为奇或偶,因此搜索时考虑每个数是奇数还是偶数,复杂度为 2 7 2^7 27
代码如下:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
typedef long long LL;
const int N = 22;
map<char,LL>q1,q2,a;
LL ans=0;
string s="BESIGOM";
void dfs(LL u,LL x)
{
    if(u==7)
    {
    //只有奇*奇*奇结果才为奇数,三项中含一个偶数乘积一定是偶数
        if((a['B']+a['E']+a['S']+a['S']+a['I']+a['E'])%2 && (a['G']+a['O']+a['E']+a['S'])%2 && (a['M']+a['O']+a['O'])%2)
            return ;
        ans+=x;
        return;
    }
    char cc=s[u];
    a[cc]=1;dfs(u+1,x*q1[cc]);
    a[cc]=2;dfs(u+1,x*q2[cc]);
}
int main()
{
    LL n;cin>>n;
    for(int i=1;i<=n;i++)
    {
        char c;LL x;cin>>c>>x;
        if(x%2) q1[c]++;
        else q2[c]++;
    }
    dfs(0,1);
    cout<<ans;
    return 0;
}

思路②:有了①的思路,可知只需要枚举 2 7 2^7 27次即可,体现在二进制上即为7个0~7个1,讨论每一位是0或者1,是0表示该位取偶数,累乘该位是偶数的情况数量;是1表示该位为奇数,累乘该位是奇数的情况。之后结果与最终结果相加。
代码如下:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
typedef long long LL;
const int N = 22;
map<char,LL>q1,q2,a;
LL ans=0;
string s="BESIGOM";
int main()
{
    LL n;cin>>n;
    for(int i=1;i<=n;i++)
    {
        char c;LL x;cin>>c>>x;
        if(x%2) q1[c]++;//c为奇数有多少个
        else q2[c]++;//c为偶数有多少个
    }
    LL res=0;
    map<char,int>v;
    for(int i=0;i<(1<<7);i++)//7个数,每个都可取奇数或者偶数,因此含2^7种情况,二进制枚举
    {
        for(int j=0;j<7;j++) v[s[j]]=i>>j&1;//依次看七位是什么,奇数为1偶数为0
        if((v['B']+v['E']+v['S']+v['S']+v['I']+v['E'])*(v['G']+v['O']+v['E']+v['S'])*(v['M']+v['O']+v['O'])%2==0)
        {
            LL sum=1;
            for(int j=0;j<7;j++)
            {
                if(i>>j&1) sum*=q1[s[j]];
                else sum*=q2[s[j]];
            }
            res+=sum;
        }
    }
    cout<<res;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值