【每日一题】codeforces 577B(1900)(抽屉原理 and DP)

本文探讨了在给定一组数的情况下,如何找到一个子序列,使其和能被特定数值整除的问题。通过分析和应用抽屉原理,文章提供了一种有效的方法来解决这个问题,并附带了一个使用动态规划的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

每日一题,坚持使我强大


今日份快乐:codeforces 577B 传送门
明天份快乐:codeforces 1348D 传送门 (感觉给自己挖了个坑)


题目大意:

给 n 个数,问是否能找到一个子序列使得他们的和可以被 m 整除

分析

如果这是一个数据规模比较小的题,直接 DP 就可以过。我们可以利用抽屉原理来消减数据范围。

抽屉原理:如果 5 个抽屉放 6 个东西,那么肯定最少要有一个抽屉放两个或更多的东西
抽屉原理在取模计算的时候有很大的意义,给个实例来理解一下
eg: n = 7, m = 7

i1234567
ai128617203436
sumi1220365373107143
sumi % 55614323

很明显 ( sum7 - sum 5 ) % 7 = 0,也就是说 (a6 + a7) % 7 = 0
这里的数据没有被 7 整除的数据,也就是说是,取余过后是七个数放在六个抽屉,最少要有两个前缀和的余数相同。
当然如果上例中出现可以被整除时,就是七个数放在七个抽屉里,虽然没余数相同的数,但是有直接满足要求的数

由上,我们就可以得出结论,当 n ≥ \geq m 时,一定存在一个序列使得他们的和被 m 整除
我们再来看当 n < m 时,因为 m ≤ \leq 1e3,,我们可以直接DP来求解

代码

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e6 + 5;
int a[maxn] = {0};
bool dp[1005][1005] = {false};

int main() {

	ios::sync_with_stdio(false);
	
	int n, m;
	cin >> n >> m;
	for(int i = 1; i <= n; i++){
		cin >> a[i];
		a[i] %= m;
	}
	
	bool ok = false;
	if(n >= m) ok = true, n = 0;  // 这里让 n = 0,下边的循环就不会执行
	
	for(int i = 1; i <= n; i++){
		dp[i][a[i]] = true;       	// 初始化
		for(int j = 0; j < m; j++){
			if(dp[i-1][j]) dp[i][j] = true;                 // 传递上一位的状态
			if(dp[i][j]) dp[i+1][(j + a[i+1]) % m] = true;  // 状态转移
		}
	}
	if(dp[n][0]) ok = true;   // 判断答案
	
	if(ok) cout << "YES" << endl;
	else cout << "NO" << endl;
	
    return 0;
}

坚持的时候很狼狈,等成功以后,丑的还是丑的🤭

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值