2020.10 小米赛第一场 A题

  先搞清楚几点:

1.题目中没有说ai是互不相同的,所以可能有多个ai;

2.题目中的目标序列是说,在目标序列中,任意两个元素都有倍数关系,即约数关系,让我们求序列的最大长度,很明显的感觉是个dp问题(这个坑我们以后再填)

(1)状态定义:我们很容易想到的是对于原序列的每一个数,都去更新他倍关系的数量,比如2就可以去4的数量,我们认为2和4可以构成一段目标序列,那么dp[4]就是2

  问题:上述思路其实是存在一定问题的,就比如2是6的约数,3是6的约数,但是2和3并不是约数关系,那么,6就需要选择最大的一个约数,而不是全部的约数,就有了下面的状态转移方程

(2)状态转移方程:dp[j] = max(dp[j],dp[i])其中j是一个较大的数,i是j的约数之一

3.有了上述思路,我们最容易想到的就是一个暴力做法,即对任何一个在原区间内的数都更新他的倍数,如果更新的数是原序列的数,就状态转移

暴力算法相对慢一点,因为一倍两倍三倍的枚举是很慢的,我们下边说一个数论优化

即对原序列中从minn -> maxx所有的数,都只乘素数倍,因为根据算术基本定理,任何数都是多个素数因子的乘积。这个是很快的,因为1e7内的素数是很少的,大约 6 * 1e5 个,而且有些很大的就超maxx了。

题目描述

In MI Intelligent Warehouse, there are nn_{}n​ products, where the ii_{}i​-th product is of size aia_iai​. We always need to box producsts into all kinds of containers, and it will be safer and more cost efficient if for any two sizes of products, one is the other's multiple, since there won't be any residual room in one container. So for each boxing we need to choose some products that for any two chosen products, either aia_iai​ is multiple of aja_jaj​ or aja_jaj​ is multiple of aia_iai​. Print the maximum number of products that can be chosen in one boxing.

输入描述:

 

The first line contains one integer n (1≤n≤2×105)n~(1\le n \le 2\times 10^5)n (1≤n≤2×105).

 

The second line contains nn_{}n​ integers a1,a2,⋯ ,an (1≤ai≤107)a_1, a_2, \cdots, a_n~(1\le a_i \le 10^7)a1​,a2​,⋯,an​ (1≤ai​≤107).

输出描述:

Only one line containing one integer, denoting the maximum number of products we can choose in one boxing.

示例1

输入

复制6 1 4 2 8 5 7

6
1 4 2 8 5 7

输出

复制4

4

说明

One possible choice is {1, 4, 2, 8}.
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int N = 1e7 + 15;
const int MAXN = 2 * 1e5 + 15;
int dp[N],num[N];
int a[MAXN];
bool book[N];
int main()
{
	int n,maxx = 0;
	cin >> n;
	for(int i = 0; i < n; i ++)
	{
		scanf("%d",&a[i]);
		num[a[i]] ++;
		maxx = max(a[i],maxx);
	}
	sort(a,a + n);
	int x;
	for(int i = 0; i < n; i ++)
	{
		x = a[i];
		if(book[x]) continue;
		book[x] = true;
		dp[x] += num[x];
		for(int j = 2; j * x <= maxx; j ++)
		{
			if(num[j * x]) dp[j * x] = max(dp[j * x], dp[x]);
		}
	}
	int res = 0;
	for(int i = 0; i < n; i ++) res = max(dp[a[i]],res);
	cout << res << endl;
	return 0;
}
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e7 + 15;
const int MAXN = 2 * 1e5 + 15;
int dp[N],num[N],primes[N],cnt;
int a[MAXN];
bool book[N];
void get_primes(int m)
{
	for(int i = 2; i <= m; i ++)
	{
		if(!book[i]) primes[cnt ++] = i;
		for(int j = 0; j < cnt && primes[j] * i <= m; j ++)
		{
			book[primes[j] * i] = true;
			if(i % primes[j] == 0) break;
		}
	}
}
int main()
{
	int n,maxx = 0;
	cin >> n;
	for(int i = 0; i < n; i ++)
	{
		scanf("%d",&a[i]);
		num[a[i]] ++;
		maxx = max(maxx,a[i]);
	}
	get_primes(maxx);
	int res = 0;
	for(int i = 1; i <= maxx; i ++)
	{
		if(!dp[i] && !num[i]) continue;
		dp[i] += num[i];
		res = max(dp[i],res);
		for(int j = 0; j < cnt && primes[j] * i <= maxx; j ++)
		{
			dp[primes[j] * i] = max(dp[primes[j] * i],dp[i]);
			res = max(dp[primes[j] * i] , res);
		}
	}
	cout << res << endl;
	return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值