数位染色(状压枚举/dfs)

在这里插入图片描述
在这里插入图片描述

(1)思路:

x的范围不超过1e18,所以x最多只有19位数字。O(n2^n)状压枚举每位选或不选即可通过。(每个位置上两种状态0或1,多少位置,2的多少次方)还需要遍历n;
代码中有注释,可以先看一遍代码,方便理解

(2)测试数据

1234567

(3)代码(状压—状态压缩)

// 状压基本模板 
#include <bits/stdc++.h>
using namespace std;
#define ll long long 
int zt[22]; //代表每一位上的数字,等下要跟状态相结合
int sum, ans;
ll num; 
// ans 是原始的数,每位数字的总和
// sum 是后面染色的数字和
// 我们的判断依据就是,若sum * 2 == ans,说明染色的数字和==未染色数字和 (仔细想想,即染色数字和就是总和的一半) 
int main () {
	int j = 0;
	cin >> num; //输入这个数字  123456
	// 将每一位拆解开
	while (num) {
		zt[j++] = num % 10; // 从最低位开始,一个一个存进zt数组中( 倒着的) 654321
		ans += num % 10; //再将每一位的数字求和 
		num /= 10; // 求下一位数字 
		
	} 
	// 最后得出的j,就知道有多少位了 
	// 枚举状态 
	// 1 << j 即二进制 左移 就是乘,右移即除,代表2^j个状态 
	for (int i = 0; i < 1 << j; ++i) {
		int p = i; // 取出i(十进制) 
		sum = 0;
		//转换成二进制(状态),并且与位置上的数字结合
		// p % 2 就是从最低位开始,取二进制数 010101... 
		// k = 0,也是最低位
		// 求和 1 代表染色,0代表不染色,染色就把和加上 
		for (int k = 0; k < j; ++k) {
			sum += p % 2 * zt[k];
			p /= 2; // 求下一位 
		} 
		if (sum * 2 == ans) {
			cout << "Yes";
			return 0;
		} 
	} 
	cout << "No";
	
	return 0;
}
  • 对二进制处理的时候,&1,>> 1,位运算比 % /更快
// 状压基本模板 
#include <bits/stdc++.h>
using namespace std;
#define ll long long 
int zt[22]; //代表每一位上的数字,等下要跟状态相结合
int sum, ans;
ll num; 
// ans 是原始的数,每位数字的总和
// sum 是后面染色的数字和
// 我们的判断依据就是,若sum * 2 == ans,说明染色的数字和==未染色数字和 (仔细想想,即染色数字和就是总和的一半) 
int main () {
	int j = 0;
	cin >> num; //输入这个数字  123456
	// 将每一位拆解开
	while (num) {
		zt[j++] = num % 10; // 从最低位开始,一个一个存进zt数组中( 倒着的) 654321
		ans += num % 10; //再将每一位的数字求和 
		num /= 10; // 求下一位数字 
		
	} 
	// 最后得出的j,就知道有多少位了 
	// 枚举状态 
	// 1 << j 即二进制 左移 就是乘,右移即除,代表2^j个状态 
	for (int i = 0; i < 1 << j; ++i) {
		int p = i; // 取出i(十进制) 
		sum = 0;
		//转换成二进制(状态),并且与位置上的数字结合
		// p % 2 就是从最低位开始,取二进制数 010101... 
		// k = 0,也是最低位
		// 求和 1 代表染色,0代表不染色,染色就把和加上 
		for (int k = 0; k < j; ++k) {
			sum += (p & 1) * zt[k];
			p = p >> 1; // 求下一位 
		} 
		if (sum * 2 == ans) {
			cout << "Yes";
			return 0;
		} 
	} 
	cout << "No";
	
	return 0;
}

(4)代码(DFS)

在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll sum;
ll x;
int getSum (ll x) {
	int res = 0;
	while (x) {
		res += x % 10;
		x /= 10;
	}
	
	return res;
} 
int judge = 0; // 找到一个方案数,正好染色的数字之和等于总和的一半,就为true 

//x 代表当前这个数字(后面几个已经选过了,还有哪些位置还没有选,
//tmp 代表选了后面几位,我会选到什么 (已经选到的数字的和)
void dfs(ll x, ll tmp) { //12345 取 13
	
	if (x == 0 || tmp * 2 >= sum) {
		if (tmp * 2 == sum) {
			judge = 1;
		}
		return ;
	}
	
//	当前x%10的数不取,直接到下一个数
	dfs(x / 10, tmp); 
//	当前x%10的数取
	dfs(x / 10, tmp + x % 10);
	
}
int main () {
	cin >> x; // 输入的数字
	sum = getSum(x); // 求位置上的数字的和 
	dfs(x, 0);
	if (judge) cout << "Yes";
	else cout << "No";
	return 0;
} 
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值