cf#319-div2-B. Modulo Sum-(dp) 求模

博客内容讲述了如何解决寻找序列中子集和为给定数的倍数问题,利用动态规划dp数组进行状态转移,确保在递推过程中不破坏原有状态,并通过tmp_dp数组避免递归中的影响。在找到和为模数倍的子集时,直接结束以避免超时。

题意要n个数中 看能否选出一个组合,使其之和为 m的倍数

开一个dp[1001]、tmp_dp[1001]

dp[i]的意思,是能得到 一个组合,其和为sum 且sum%m==i 那么我们就让dp[i]=1;表示存在

在递推的过程中,如果我们在前i个数得到dp[j]=1;也就是存在sum1%m==j;

那么对于a[i+1]、我们可以推得  一个sum2=sum1+a[i+1]、所以得到sum2%m==k; 也就是dp[k]=1;

即: 由在前i次循环中,如果得到dp[j]=1;可以在第i+1次循环得到  dp[   ( j+a[i+1] )%m   ]=1;

这里为什么强调前i次循环和第i+1次循环? 因为要得到dp[k]=1,其条件中的dp[j]=1;必须在 a[i+1]的递推之前就已经得到

这里给个反例,  a[1]=1,m=100如果我从a[1]=1;推得dp[1%100]=dp[1]=1; 然后我又根据这个dp[1]=1得到dp[(1+a[1])%100]=dp[2]=1;这样就一直推下去了

所以我们在从dp[j]推到dp[k]的时候,dp[k]应该记录在一个tmp_dp[k]数组里(为了不在递归中影响dp数组的值)

做完整个递推后,才把tmp_dp[]的值赋给dp;


还有一点是dp[0]就意味有 sum%m==0,也就是答案找到了,此时直接break,否则超时;


#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <list>
#include <set>
#include <vector>
using namespace std;


int dp[1005];
int tm[1000005];
int tmp_dp[1005];//主函数中解释;
int main()
{
	
	int n,m;
	int i,j;
	scanf("%d%d",&n,&m);
	
	for (i=1;i<=n;i++)
		scanf("%d",&tm[i]);
 

	for (i=1;i<=n;i++)
	{

		if (dp[0]) break;
		for (j=1;j<m;j++)
		{
			if (dp[j])//此处dp[j]为上次循环所留下的结果 
			{
			 
				tmp_dp[(j+tm[i])%m]=1;//如果直接改dp数组的值,上面的判断会出问题,会出现利用"自己得到自己的两倍"这样的情况				 
			}//此处意思是,如果dp[j]存在,即能组成 一个sum,使得sum%m=j,那么sum+tm[i]得到sum2,所以sum2%m也是可以得到的*最终目标是求sumX%m==0
		//	也就是有整除m的数存在就可以停止循环了,算是有点贪心吧。

		}
		tmp_dp[tm[i]%m]=1; 
		for (j=0;j<m;j++)
		dp[j]=tmp_dp[j];
	}
	
	if (dp[0])
		printf("YES\n");
	else
		printf("NO\n"); 
	return 0;
	
}


<think> 首先,用户的问题是关于Python中的`setdefault`方法,特别是它的重载含义,以及与其他可变映射子类如`OrderedDict`、`ChainMap`、`WeakKeyDictionary`的`setdefault`方法的对比。 我需要回顾系统级指令: - 所有行内数学表达式使用`$...$`格式。 - 独立公式使用`$$...$$`格式并单独成段。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 - 回答结构清晰,帮助用户逐步解决问题。 用户在问题中提到了引用[1]到[5],但我需要基于我的知识和这些引用来回答。引用内容是关于dict和set的效率、信号处理、产品类、运算符重载、dict计数函数的比较等。这些引用可能与用户的问题相关,但不直接涉及`setdefault`方法。 关键引用: - 引用[1]:讨论了dict和set的效率、无序性问题,提到了哈希表。 - 引用[5]:展示了两种计数函数,提到了`dc.get(item, 0)`,这与`setdefault`相似但不相同。 用户的问题焦点是`setdefault`方法的重载和与其他类的对比。我需要解释: - 什么是`setdefault`方法。 - 它如何在Python中工作。 - 重载的含义:可能是指自定义类的重写或覆盖。 - 与其他类如`OrderedDict`、`ChainMap`、`WeakKeyDictionary`的对比。 结构化回答: 1. **解释`setdefault`方法**:在字典中,`dict.setdefault(key, default)`方法返回指定键的值。如果键不存在,则设置键的值为默认值,并返回默认值。 2. **重载的含义**:在Python中,重载通常指在子类中重写父类的方法。这里是说在自定义类中重写`setdefault`方法。 3. **对比其他类**: - `OrderedDict`:在collections块中,保持键的插入顺序。它的`setdefault`方法行为类似于dict,但维护顺序。 - `ChainMap`:在collections块中,连接多个映射。`setdefault`方法只影响第一个映射。 - `WeakKeyDictionary`:在weakref块中,用于弱引用键。`setdefault`方法可能涉及弱引用管理。 4. **引用相关部分**:例如,引用[1]提到哈希表,这与dict实现相关;引用[5]提到计数函数,其中`get`方法类似于`setdefault`但不同。 5. **使用中文**:整个回答用中文。 6. **生成相关问题**:在回答结束时添加相关问题。 逐步回答: - 第一步:介绍`setdefault`方法。 - 第二步:解释重载的含义。 - 第三步:对比其他可变映射子类。 - 第四步:总结。 确保语法正确:使用LaTeX格式行内数学表达式,如果有数学元素。但在这个问题中,可能没有数学内容,所以我需要小心。 可能的数学内容:如果有方程或变量,例如在描述行为时。 生成相关问题: 1. Python中如何自定义字典类? 2. OrderedDict、ChainMap、WeakKeyDictionary分别在何时使用? 3. setdefault与get方法的区别是什么? 引用标识:在引用相关内容时添加[^1]、[^2]等。基于用户提供的引用。 例如: - 引用[1]说“集合底层的哈希表”,所以当讨论dict底层时,可以添加[^1]。 - 引用[5]提到计数函数,当讨论get和setdefault的区别时,可以添加[^5]。 完整回答结构: 1. 开头:直接回答问题。 2. 主体:分步解释。 3. 代码示例:如果有必要,展示代码。 4. 结束:生成相关问题。 </think> ### Python 中 `setdefault` 方法的重载与可变映射子类的对比 在 Python 中,`setdefault` 方法是 `MutableMapping` 接口(可变映射类型)的一部分,主要用于字典(`dict`)及其子类。其核心功能是:如果键存在,返回对应的值;如果键不存在,则设置键的值为指定的默认值,并返回该默认值。本回答将逐步解释 `setdefault` 方法的重载含义,并对比其与 `OrderedDict`、`ChainMap`、`WeakKeyDictionary` 等 `MutableMapping` 子类中同名方法的关联和差异。参考了您提供的引用内容(如 dict 的哈希表实现和计数器示例),我会确保回答清晰可靠。 #### 1. **`setdefault` 方法的基本原理** - `setdefault(key, default=None)` 方法的语法:当调用 `d.setdefault(key, default)` 时: - 如果 `key` 存在于字典 `d` 中,返回 `d[key]`。 - 如果 `key` 不存在,则设置 `d[key] = default`,并返回 `default`。 - **目的**:避免在检查键存在性时出现冗余代码,简化键值管理。例如,在计数器函数中,引用[5]展示了类似逻辑:`dc[item] = dc.get(item, 0) + 1`,但 `setdefault` 更简洁,因为它直接在键不存在时设置默认值[^5]。 - **底层机制**:Python 的 dict 基于哈希表实现,`setdefault` 依赖于哈希查找,确保平均时间复杂度为 $O(1)$。这与引用[1]中提到的 dict 效率和无序性相关,因为哈希表在插入元素时会动态调整桶的大小以保持高效[^1]。 #### 2. **`setdefault` 方法重载的含义** - **重载(Overloading)**:在 Python 中,重载通常指在子类中重写(override)父类的方法,以改变默认行为或添加新功能。这里,“重载”指的是在自定义类中覆盖 `MutableMapping` 的 `setdefault` 方法: - **为什么需要重载**:当自定义映射类型(如扩展 `dict` 或实现 `MutableMapping`)时,可能需要修改 `setdefault` 的逻辑,以适应特定需,例如添加日志、验证输入或改变存储策略。 - **实现方式**:在自定义类中定义 `def setdefault(self, key, default=None): ...` 方法。Python 的运算符重载特性(如引用[4]所述)允许我们以少量代码实现类似行为,但 `setdefault` 是方法而非运算符[^4]。 - **示例代码**:以下是一个自定义字典类重载 `setdefault` 的例子,添加了简单日志: ```python class CustomDict(dict): def setdefault(self, key, default=None): print(f"Setting default for key: {key}") # 自定义行为 return super().setdefault(key, default) # 调用父类方法 d = CustomDict() d.setdefault(&#39;a&#39;, 100) # 输出: Setting default for key: a ``` - **重载的关联**:所有 `MutableMapping` 子类(如 `dict`、`OrderedDict` 等)都继承自同一个基类,因此重载 `setdefault` 时,方法签名和行为应保持一致,确保接口统一性。但具体实现可因类而异。 #### 3. **与其他 `MutableMapping` 子类的 `setdefault` 方法对比** Python 的 `collections` 和 `weakref` 块提供了多种 `MutableMapping` 子类,每个子类的 `setdefault` 方法在行为上略有不同,取决于其数据结构特性。以下是对比表格,便于理解: | 类名 | 所属块 | `setdefault` 方法的独特行为 | 与标准 `dict.setdefault` 的关键差异 | 适用场景示例 | |------|----------|-----------------------------|-------------------------------------|--------------| | **`dict`** | 内置 | - 默认实现:如果键不存在,设置并返回默认值。<br>- 时间复杂度:平均 $O(1)$,基于哈希表[^1]。 | 基准行为,无序存储键值对。 | 通用键值存储,如计数器(类似引用[5]的`count_get`函数)[^5]。 | | **`OrderedDict`** | `collections` | - 继承 `dict.setdefault` 的核心逻辑,但**维护键的插入顺序**。<br>- 调用 `setdefault` 时,如果键不存在,新键会被添加到末尾,不影响现有顺序。 | 有序性:键的顺序基于添加时间(如引用[1]解释的无序问题,`OrderedDict` 是有序的替代方案)[^1]。 | 需要记录插入顺序的场景,如最近使用缓存(LRU)。 | | **`ChainMap`** | `collections` | - **只影响第一个映射**:`setdefault` 仅在链中的第一个字典(top-most mapping)上操作。如果键在所有映射中都不存在,则添加到第一个字典。<br>- 返回第一个映射中找到的值或设置的默认值。 | 链式查找:方法不会遍历整个链,只处理第一个映射。 | 合并多个字典(如配置覆盖),优先级由链顺序决定。 | | **`WeakKeyDictionary`** | `weakref` | - **键是弱引用**:`setdefault` 设置值时,键不会被强引用,允许垃圾回收(GC)移除键值对。<br>- 如果键被回收,调用 `setdefault` 可能引发 `KeyError` 或返回默认值。 | 弱引用管理:行为类似 `dict.setdefault`,但键的生命周期受 GC 影响。 | 缓存大型对象,避免内存泄漏(键不阻止对象被回收)。 | **关键对比总结**: - **共同点**:所有子类都遵循 `MutableMapping` 接口,`setdefault` 方法签名相同(`key, default=None`),并保证原子性操作(设置和返回在单一步骤中完成)。 - **差异点**: - **顺序性**:`OrderedDict` 强调插入顺序,而 `dict` 和 `WeakKeyDictionary` 无序。 - **范围影响**:`ChainMap` 只修改链的第一个映射,其他类修改自身。 - **内存管理**:`WeakKeyDictionary` 涉及弱引用,适合敏感内存应用。 - **性能考虑**:所有类的平均时间复杂度均为 $O(1)$,但常数因子可能因实现而异(例如,`OrderedDict` 的双向链表会略微增加开销)。 #### 4. **实际应用建议** - **何时使用 `setdefault`**:在需要“检查并设置”式的场景中,使用 `setdefault` 比先调用 `get` 再赋值更高效和简洁(对比引用[5]中的 `count_get` 函数)[^5]。 - **重载最佳实践**:在自定义类中重载 `setdefault` 时,务必调用 `super()` 来复用父类逻辑,避免破坏哈希表性能(基于引用[1]的哈希表原理)[^1]。 - **注意事项**:`setdefault` 的默认值参数如果为可变对象(如列表),可能导致意外共享;改为使用 `default=None` 并在代码中初始化可变对象更安全。 通过以上分析,`setdefault` 方法的重载是 Python 面向对象编程的灵活体现(如引用[4]所述),允许在子类中定制行为,同时保持与核心映射类的兼容性[^4]。如果您有具体代码示例,我可以进一步优化解释。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值