Cryptarithmetic Problem ‘ODD+ODD == EVEN’;map()函数,reduce()

本文介绍了一种解决Cryptarithmetic问题的方法,通过编写Python代码找出字母方程(如'ODD+ODD==EVEN')对应的数字解。文章详细解释了代码的工作原理,并演示了如何验证解的有效性。

Cryptarithmetic Problem ‘ODD+ODD == EVEN’

这段代码不仅仅是针对一个‘ODD+ODD==EVEN’,更重要的是它可以针对很多这种类型的等式,来寻找可能的数字数字表示。例如:‘655+655=1310’

from __future__ import division     #在python2版本中,3/2返回1,而在python3版本中却返回1.5,在这个问题中,我们需要1.5作为结果,但是我们又用的是python2,故添加这一句来转化
import string, re, itertools

def solve(formula):
    """Given a formula like 'ODD + ODD == EVEN', fill in digits to solve it.
    Input formula is a string; output is a digit-filled-in string or None."""
    for f in fill_in(formula):
        if valid(f):
            return f
    
def fill_in(formula):
    "Generate all possible fillings-in of letters in formula with digits."
    letters = ''.join(set(re.findall('[A-Z]',formula))) #should be a string
    for digits in itertools.permutations('1234567890', len(letters)):
        table = string.maketrans(letters, ''.join(digits))
        yield formula.translate(table)
    
def valid(f):
    """Formula f is valid if and only if it has no 
    numbers with leading zero, and evals true."""
    try: 
        return not re.search(r'\b0[0-9]', f) and eval(f) is True
    except ArithmeticError:
        return False

注解:

  1. valid()函数:判断一个数是否是正确格式的数,若是,则返回True,否则返回False。该函数把以0开头的数字去除掉了,因为以0开头的数字在计算机中是以八进制进行识别的,不符合我们解决这个问题的规则。
  2. fill_in()函数:该函数是一个生成器函数,返回一个数字等式对应于输入的字母等式字符串。
    (i)若输入是‘ODD+ODD==EVEN’,那么letters=‘ODEVN’这样一个字符串。join是字符串的一个方法,把join括号里面的东西连接成一个字符串。
    (ii)**set()**返回一个集合,集合的概念类似我们高中时期学的数学概念集合,有一篇博客写得不错,适合入门set,如果有对set不是很清楚地可以参考:
    https://www.cnblogs.com/SmallWZQ/p/8488744.html
    (iii)re.findall(’[A-Z]’,formula) 这一部分查找formula里面所有的大写字母,这一部分是正则表达式模块的内容
    (iv)itertools.permutations(‘1234567890’,len(letters)),这一部分返回的是所有len(letters)长度的数字的全排列。对这个例子来讲,letters的长度是5,所以我们从0-9这10个数字中任选5个全排列,返回所有的可能性。
    (v)maketrans(from,to),该方法接收两个参数,返回一个转换表。该表将把from中的每个字符映射到to中相同位置的字符中;from和to的长度必须相同。
    (vi)string.translate(table,delete_string), 该方法接受一个由上述maketrans()方法创建的table和一个将要被删除的字符串,先将string中匹配到的delete_string删除掉,然后再将string中的‘from’替换成‘to’(from和to如前所述)。当然,第二个参数可以省略。
  3. slove()函数:检查fill_in()函数返回的等式中的数字是否符合正确的数字规则。
  4. 上述代码仅仅返回了一种可能性,若是把所有的可能性都打印出来,那么我们只需要把solve()函数中的return改成print就可以了,因为外层是一个for循环,他会一直打印结果直到结束,而不是碰到第一个结果就return了。

把‘YOU’变成‘(1*U+10*O+100*Y)

def compile_word(word):
    """Compile a word of uppercase letters as numeric digits.
    E.g., compile_word('YOU') => '(1*U+10*O+100*Y)'
    Non-uppercase words uncahanged: compile_word('+') => '+'"""
    
    if word.isupper():
        terms = [('%s*%s' % (10 ** i, d))
                 for (i, d) in enumerate(word[::-1])]
        return '(' + '+'.join(terms) + ')'
    else:
        return word

map()

  1. map() 是python内置的高阶函数,他接受一个函数f和一个或多个list,并通过把函数f依次作用在list的每个元素上,得到一个新的object并返回。不改变原来的list(注:python2返回列表,python3返回迭代对象即object)
res = map(compile_word, ['YOU', 'ME', 'you'])
print(res)
print(list(res))
>>><map object at 0x000001B2E47EE908>  # 迭代对象
>>>['(1*U+10*O+100*Y)', '(1*E+10*M)', 'you']  # 将其转化为列表
# 也可以使用lambda匿名函数
print(list(map(lambda x: x**2, [1,2,3])))
print(list(map(lambda x,y: x+y, [1,3,5], [2,4,6])))
>>>[1, 4, 9]
>>>[3, 7, 11]

reduce()

说完map(),不得不说一下reduce();只针对python2,在python3中,移除了reduce(),并把它放在了functools模块里,用的时候只需要from functools import reduce 即可

  1. reduce() 函数会对参数序列中元素进行累积。
  2. reduce() 函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。
def add(x, y) :            # 两数相加
     return x + y
reduce(add, [1,2,3,4,5])   # 计算列表和:1+2+3+4+5
>>> 15
reduce(lambda x, y: x+y, [1,2,3,4,5])  # 使用 lambda 匿名函数
>>> 15
reduce(add, [1,2,3,4,5],100)  # 可接受第三个参数作为初始值

你必须对该题目采用记忆化搜索!!!!!!!!!!!!我的代码为什么结果输出不对#include<bits/stdc++.h> using namespace std; unordered_map<string,int> mp; int t,n; string s; int dfs(string s) { if(mp.count(s))return mp[s]; if(s.size()==1)return mp[s]=1; string l=s,r=s; int res=0; int a=s.find("01"),b=s.find("10"); if(a==-1&&b==-1)return mp[s]=0; if(a!=-1) { l.replace(a,2,"1"); res+=dfs(l); } if(b!=-1) { r.replace(b,2,"0"); res+=dfs(r); } return mp[s]=res; } int main( ) { cin>>t; while(t–) { cin>>n>>s; cout<<n+dfs(s)<<‘\n’; } return 0; }MC0320 狠狠地对字符串做你想做的事吧 难度: 黄金 时间限制:1秒 占用内存:128 M 收藏 报错 对一个字符串 S S ,有以下两种操作: • 将子串 01 替换为 1 • 将子串 10 替换为 0 总共 t t组数据,每组的 S S都是一个给定长度为 n n的 01 串。 求每组数据中, S S一共有多少个子串,能经过若干次操作,变成长度为1的字符串。 格式 输入格式: 第一行包含一个整数 t ( 1 ≤ t ≤ 1000 ) t(1≤t≤1000) 表示 t t组数据; 每组的第一行包含一个整数 n n表示 S S的长度; 每组的第二行包含一个由 n n个字符 S 1 , S 2 , … , S n S 1 ​ ,S 2 ​ ,…,S n ​ 组成的二进制字符串 S S( 其中 S i 0 S i ​ =0或 S i 1 S i ​ =1)。 输出格式: 针对每个测试用例,输出该字符串满足题意的子串个数。 样例 1 输入: 5 1 1 2 01 3 100 4 1001 5 11111 复制 输出: 1 3 4 8 5 复制 备注 其中: 1 ≤ t ≤ 1000 , 1 ≤ n ≤ 2 × 1 0 5 , ∑ n ≤ 2 ∗ 1 0 5 1≤t≤1000,1≤n≤2×10 5 ,∑ n ​ ≤2∗10 5 ,保证所有测试用例的 n 总和不超过 2 × 1 0 5 2×10 5 。
最新发布
08-22
<think>好的,我现在要解决的问题是,使用C++中的while循环来计算给定正整数n的奇数和与偶数和。首先,我需要理解问题的要求。用户希望输入一个n值,然后分别计算从1到n的所有奇数的和以及偶数的和。例如,当n为9时,奇数和是25,偶数和是20。我需要用while循环来实现这一点,而不是其他循环结构比如for循环。 首先,我需要确定如何遍历从1到n的所有数字。因为要使用while循环,所以需要初始化一个计数器变量,比如i,从1开始,然后在每次循环中递增,直到i超过n为止。在循环体内,我需要检查当前的i是奇数还是偶数,然后分别累加到对应的和中。 接下来,如何判断一个数是奇数还是偶数?这可以通过取模运算来实现。如果i % 2等于0,那么是偶数;否则是奇数。或者也可以用位运算,比如i & 1,结果为1则是奇数,0则是偶数。不过这里用取模可能更直观。 然后,我需要初始化两个变量来保存奇数和和偶数和,比如sum_odd和sum_even,初始值都为0。在每次循环中,根据i的奇偶性,将i加到相应的和中。 现在,考虑循环的条件。初始化i为1,然后循环继续的条件是i <=n。在每次循环结束时,i自增1。这样就能遍历1到n的所有整数。 接下来,需要考虑输入输出的部分。用户输入的n应该在1到1000之间,所以在程序中应该添加输入验证,确保n在这个范围内。不过问题描述中已经说明n的范围是1到1000,可能不需要处理输入错误的情况,但可能需要确保输入的合法性。 例如,用户输入n=9时,程序应该正确输出奇数和25,偶数和20。测试这个例子是否正确可以验证程序的正确性。 现在,开始编写代码的大纲: 1. 读取n的值,确保在1到1000之间。如果用户输入不符合要求,可能需要提示重新输入,但题目可能不要求处理这部分,所以可能直接读取n的值。 2. 初始化sum_odd和sum_even为0。 3. 初始化i=1。 4. 使用while循环,条件为i <=n。 5. 在循环中,判断i的奇偶性。 6. 根据判断结果,将i加到相应的和中。 7. 循环结束后,输出两个和的值。 现在,具体代码的实现: 首先,包含必要的头文件,比如iostream。 然后,主函数中: 读取n的值: int n; std::cin >> n; 然后初始化sum_odd和sum_even为0,i为1: int sum_odd = 0; int sum_even = 0; int i = 1; 然后进入while循环: while(i <= n) { if(i % 2 == 0) { sum_even += i; } else { sum_odd += i; } i++; } 之后输出结果: std::cout << "奇数和:" << sum_odd << std::endl; std::cout << "偶数和:" << sum_even << std::endl; 这样就能得到正确的结果。 现在,测试一下n=9的情况: i从1到9: i=1,奇数,sum_odd=1 i=2,偶数,sum_even=2 i=3,奇数,sum_odd=4 i=4,偶数,sum_even=6 i=5,奇数,sum_odd=9 i=6,偶数,sum_even=12 i=7,奇数,sum_odd=16 i=8,偶数,sum_even=20 i=9,奇数,sum_odd=25 循环结束,输出正确的结果。 那为什么不用i从1开始,每次加2的方式来计算奇数和偶数和呢?比如,先计算奇数的和,可以循环i从1到n,每次加2,但这样可能需要两个循环。或者,偶数的和可以从2开始,每次加2。不过题目要求用while循环,而用户的问题没有限制只能使用一个循环。但当前的方法是遍历每个数,逐个判断,这样更简单,且只需要一个循环,可能更符合题目的要求。 不过,如果使用两次循环,一个计算奇数,另一个计算偶数,可能效率更高。例如: 计算奇数和: int i =1; while(i <=n) { sum_odd +=i; i +=2; } 然后计算偶数和: i=2; while(i <=n) { sum_even +=i; i +=2; } 这样,两个循环分别处理奇数和偶数,可能更快,因为循环次数减少了一半。例如,当n=9时,奇数的循环执行5次,偶数的循环执行4次,而原来的方法需要9次循环。不过对于n=1000来说,两种方法的时间差异可以忽略不计。但题目要求用while循环,而这里给出的方法是否更优呢?或者题目是否允许使用两个循环? 但用户的问题可能更倾向于使用一个循环,因为题目中的示例可能更自然。所以可能还是采用原来的方法更合适,即一次循环中同时处理奇偶判断。 现在,关于输入验证的问题,用户给出的n的范围是1到1000,所以代码中是否需要确保输入在这个范围?例如: do { std::cout << "请输入n(1<=n<=1000):"; std::cin >>n; } while(n <1 || n>1000); 这样可以确保输入的n有效。但题目可能没有明确要求,因此可能不需要处理输入验证,直接假设输入是有效的。 总结,最终的代码应该是: #include <iostream> int main() { int n; std::cin >> n; int sum_odd = 0, sum_even = 0; int i = 1; while (i <= n) { if (i % 2 == 1) { sum_odd += i; } else { sum_even += i; } i++; } std::cout << sum_odd << std::endl; std::cout << sum_even << std::endl; return 0; } 或者输出时根据题目要求的格式,比如样例中的输出是两行,奇数和和偶数和各占一行。比如,输入9的话,输出25和20,各占一行。所以代码中的输出应该直接输出两个数,换行即可。 现在,考虑i的初始化和循环条件是否正确。例如,当n=1时,sum_odd=1,sum_even=0,这正确。当n=2时,sum_odd=1,sum_even=2,正确。 现在,问题已经解决,代码正确。</think><< 要计算1到n的奇数和与偶数和,可以使用while循环遍历每个数字并根据奇偶性累加。以下是实现代码: ```cpp #include <iostream> int main() { int n; std::cin >> n; // 输入正整数n int sum_odd = 0, sum_even = 0; int i = 1; // 从1开始遍历 while (i <= n) { if (i % 2 == 1) { // 判断奇数 sum_odd += i; } else { // 偶数 sum_even += i; } i++; // 更新计数器 } std::cout << "奇数和:" << sum_odd << std::endl; std::cout << "偶数和:" << sum_even << std::endl; return 0; } ``` **解释**: 1. **初始化变量**:`sum_odd`和`sum_even`分别记录奇数和与偶数和,初始值均为0。 2. **循环遍历**:从`i=1`开始,通过`while`循环逐个检查1到n的每个数字。 3. **奇偶判断**:使用`i % 2`判断奇偶性,余数为1是奇数,否则是偶数。 4. **累加求和**:根据奇偶性将当前值累加到对应的和中。 5. **输出结果**:循环结束后输出两个和的值。 **示例输入/输出**: 输入 `9`,输出: ``` 奇数和:25 偶数和:20 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值