2019校招真题在线编程 k倍多重正整数集合 (树形DP)

题目描述

k倍多重正整数集合的定义是:在一个多重集合(元素可以重复)中,不存在一个正整数是另一个正整数的k倍。

现在小M有n个正整数,你可以选择其中一些数构成k倍多重正整数集合。请求出最多能选出多少数来构成它。

输入描述:

第一行有两个整数n, k(1 <= n <= 10^5, 1 <= k <= 10^9)。

接下来一行有n个正整数a1, a2, ..., an (1 <= ai <= 10^9)。

输出描述:

最多能选出多少数构成k倍多重正整数集合。

示例1

输入

复制

6 2
2 3 6 5 4 10

输出

复制

3

说明

第一个样例中,我们选择2,3,5即可满足要求,因为k == 2,不存在一个数是另一个数的两倍。

示例2

输入

复制

2 2
2 2

输出

复制

2

说明

第二个样例中,我们选择2,2即可满足要求,因为k == 2,2也不是2的两倍,注意多重集合元素可以重复。

题解:假设dp[i][1] 表示选第i个数能够获得的最大值,dp[i][0]表示不选第i个数能够获得的最大值。

#include <bits/stdc++.h>

using namespace std;

const int N = (int)1e5 + 10;

int a[N];
int cot[N];
int flag[N];
int mark[N];
int dp[N][2];
map <int, int> mp;
vector <int> vec[N];

//dp[i][1]表示选第i个数获得最大值,dp[i][0]表示不选第i个数获得的最大值。

void dfs(int u) {
	mark[u] = 1;
	dp[u][1] = cot[u];
	dp[u][0] = 0;
	for (int v: vec[u]){
		if(!mark[v]) {
			dfs(v);
			dp[u][1] = max(dp[u][1] + dp[v][0], dp[u][1]);
			dp[u][0] += max(dp[v][1], dp[v][0]);
		}
	}
}


int main() {
	int n, k;
	cin >> n >> k;
	for (int i = 0; i < n; i++) {
		scanf("%d", a + i);
	}
	sort(a, a + n);
	int h = 0;
	cot[h] = 1;//表示第i个数的个数。
	for (int i = 1; i < n; i++) {//去重
		if(a[i] == a[h]){
			cot[h]++;
		} else {
			a[++h] = a[i];
			cot[h]++;
		}
	}
	if (k == 1) {
		cout << h << "\n";
		return 0;
	}
	for (int i = 0; i <= h; i++) {
		mp[a[i]] = i;
		if (a[i] % k == 0 && mp.count(a[i] / k) > 0) {
			//将有倍数关系的两个数加到树上。
            vec[mp[a[i] / k]].push_back(i);
			flag[mp[a[i] / k]] = 1;
			flag[i] = 1;
		}
	}
	int cnt = 0;
	for (int i = 0; i <= h; i++) {
		if (!flag[i]) {
			cnt += cot[i];
		} else if (!mark[i]) {
			dfs(i);
			cnt += max(dp[i][0], dp[i][1]);
		}
	}
	cout << cnt << "\n";
	return 0;	
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值