fzu 1912 Divisibility by Thirty-six(数论+枚举)

本文介绍了一道编程题的解决方法,题目要求从一组数字中选取部分构成能被36整除的最大数字。通过分析36的因数特性,采用枚举与深度优先搜索结合的方法,实现高效求解。

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

题目链接:fzu 1912 Divisibility by Thirty-six


题目大意:给出一串数字,从中任选n个数字组成一个n位数可以被36整除。(0也算符合条件)


解题思路:首先36可以拆分成9*4,然后根据列举9的倍数可以发现,凡是可以整除9的数,各个位上数字之和一定是9的倍数。这样就可以反向推到,只要剔除若干个数字,使得原有序列数值之和保证为9的倍数既可以满足说可以整除9.

而4的倍数又很容易枚举。


所以就有了完整的解题思路,首先先枚举最后两位,使得整个数列可以被4整除(总共就22种,04和40肯定选用40,类似的有84和80),然后就是枚举踢掉几个数字,然后用dfs找出满足情况,其中加一些剪枝。


#include <stdio.h>
#include <string.h>
#include <algorithm>

using namespace std;

const int N = 1005;
const int M = 15;
const int dir[30][2] = { {0, 0}, {4, 0}, {8, 0}, {1, 2}, {1, 6}, {2, 0}, {2, 4}, {2, 8}, {3, 2}, {3, 6}, {4, 4}, {8, 4}, {5, 2}, {5, 6}, {6, 0}, {6, 4}, {6, 8}, {7, 2}, {7, 6}, {8, 8}, {9, 2}, {9, 6} };

int tmp, rec[M], cnt[M];
int n, s[N], x, id;
bool flag;
char ans[N];

void init() {
	char num[N];
	scanf("%s", num);
	x = strlen(num), tmp = 0;

	flag = false;
	memset(rec, 0, sizeof(rec));
	memset(ans, 0, sizeof(ans));

	for (int i = 0; i < x; i++) {
		int cur = num[i] - '0';
		tmp += cur;
		rec[cur]++;
	}
}

void put(int a[], int d, char *str) {
	int len = 0;
	for (int i = 9; i >= 0; i--) {
		for (int j = 0; j < a[i]; j++)
			str[len++] = i + '0';
	}
	
	for (int i = 0; i < 2; i++)
		str[len++] = dir[d][i] + '0';
	str[len] = '\0';
}

bool cmp(char *a, char *b) {
	for (int i = 0; a[i]; i++) {
		if (a[i] != b[i]) return a[i] < b[i];
	}
	return false;
}

void judge(int m) {

	char now[N];
	put(cnt, id, now);

	if (m < x || (cmp(ans, now) && m == x)) {
		flag = true;
		x = m;
		strcpy(ans, now);
	}
}

void dfs(int m, int d, int sum) {

	if (d >= m) {
		if ((sum - tmp) % 9 == 0)	judge(m);
		return;
	}

	for (int i = 1; i <= 8; i++) {
		if (!cnt[i]) continue;
		cnt[i]--;
		dfs(m, d + 1, sum + i);
		cnt[i]++;
	}
}

void search(int d) {
	for (int i = 0; i < 2; i++) {
		cnt[dir[d][i]]--;
		if (cnt[dir[d][i]] < 0) return;
	}

	id = d;
	for (int i = 0; i <= tmp && i <= x; i++)
		dfs(i, 0, 0);
}

bool isOK(char *str) {
	int sum = 0, len = strlen(str);
	if (len == 0 && rec[0] == 0) return false;
	for (int i = 0; i < len; i++)
		sum += str[i] - '0';
	return !sum;
}

void solve() {

	for (int i = 0; i < 22; i++) {
		memcpy(cnt, rec, sizeof(rec));
		search(i);
	}

	if (isOK(ans)) printf("0\n");
	else if (flag) puts(ans);
	else printf("impossible\n");
}

int main() {
	int cas;
	scanf("%d", &cas);
	for (int i = 1; i <= cas; i++) {

		init();
		printf("Case %d: ", i);

		solve();
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值