CF1550A Find The Array

题意

我们称一个数列 a1,a2,…,ana_1,a_2,…,a_n 是美丽的,当且仅当数列中每个元素 aia_i 至少满足如下三个条件之一:

  • ai=1a_i=1。

  • ai−1a_i-1 在这个数列中。

  • ai−2a_i-2 在这个数列中。

现在给你一个整数 ss,求出一个元素和为 ss 的美丽的数列至少要几个元素?

做法1:贪心

要求最少的元素个数,我们就让放入的数尽量大,即每次都放比前面的数大 22 的数,直到放满或溢出为止。

证明

  • 若放满,自然满足题目要求。

  • 若溢出,设最后放入的数为 ana_n,此时与要求的元素和 ss 的差为 c=∑i=1nai−sc=\sum_{i=1}^na_i-s,可令 an⇒an−ca_n\Rightarrow a_n-c,由我们之前的计算得,这时的 an≥1a_n≥1,无论 ana_n 为什么数,必然有一个 aia_i 满足 ∣ai−an∣≤2\left|a_i-a_n\right|≤2,也满足了题意。

Code1

#include<bits/stdc++.h>
using namespace std;
int t,s,sum,ans;
int main()
{
	cin>>t;
	while(t--)
	{
		cin>>s;
		sum=ans=0;
		for(int i=1;sum<s;i+=2)//一直放直到放满或溢出 
		{
			sum+=i;
			ans++;
    	}
    	cout<<ans<<endl;
	}
	return 0;
}

做法2:动态规划

设 dpi,jdp_{i,j} 表示一个和为 ii,最后放的数为 jj 的美丽的数列最少需要几个元素。同时,设我们答案的美丽序列是按顺序排列的,即 a1≤a2≤…≤ana_1≤a_2≤…≤a_n。

我们提前要做一下处理,即

dpi,j={infj!=1ij=1dp_{i,j}=\begin{cases}inf&j!=1\\i&j=1\end{cases}

其中 0≤j≤i≤s0≤j≤i≤s。

也就是我们提前放好 11,后面只需要处理一般情况,因为要取最小值,所以其他的设为无穷大。

接下来对于 dpi,jdp_{i,j},我们有三种选择:

  • 前一个放了 jj,此时再放一个 jj:dpi,j=min⁡(dpi,j,dpi−j,j+1)dp_{i,j}=\min(dp_{i,j},dp_{i-j,j}+1),因为之前放了 jj,所以需满足之前的和 i−j≥ji-j≥j。

  • 前一个放了 j−1j-1,此时再放一个 jj:dpi,j=min⁡(dpi,j,dpi−j,j−1+1)dp_{i,j}=\min(dp_{i,j},dp_{i-j,j-1}+1),同上,需满足之前的和 i−j≥j−1i-j≥j-1。

  • 前一个放了 j−2j-2,此时再放一个 jj:dpi,j=min⁡(dpi,j,dpi−j,j−2+1)dp_{i,j}=\min(dp_{i,j},dp_{i-j,j-2}+1),同上,需满足之前的和 i−j≥j−2i-j≥j-2。

于是可以得出状态转移方程:

dpi,j=min⁡{dpi−j,j+1i−j≥jdpi−j,j−1+1i−j≥j−1dpi−j,j−2+1i−j≥j−2dp_{i,j}=\min\begin{cases}dp_{i-j,j}+1&i-j≥j\\dp_{i-j,j-1}+1&i-j≥j-1\\dp_{i-j,j-2}+1&i-j≥j-2\end{cases}

其中 2≤j≤i≤s2≤j≤i≤s,j=1j=1 的情况前面已经计算过。

做完上面的计算后,我们用 ansans 数组记录答案,即 ansians_i 表示和为 ii 时的答案,显然 ansi=min⁡j=1idpi,jans_i=\min_{j=1}^idp_{i,j}。

最后询问时,直接输出答案即可。总体时间复杂度为 O(N2)O(N^2)。

Code2

#include<bits/stdc++.h>
using namespace std;
const int N=5010,inf=1e9;
int t,s,dp[N][N],ans[N];
int main()
{
    cin>>t;
    //预处理 
    for(int i=0;i<5001;i++)
		for(int j=0;j<=i;j++)
			if(j==1)
    			dp[i][j]=i;
    		else
    			dp[i][j]=inf;
    for(int i=1;i<5001;i++)//j为1时的答案 
    	ans[i]=dp[i][1];
    for(int i=2;i<5001;i++)
    {
    	for(int j=2;j<=i;j++)
    	{
    		//三种情况 
    		if(i-j>=j)
    			dp[i][j]=min(dp[i-j][j]+1,dp[i][j]);
    		if(i-j>=j-1)
    			dp[i][j]=min(dp[i-j][j-1]+1,dp[i][j]);
    		if(i-j>=j-2)
    			dp[i][j]=min(dp[i-j][j-2]+1,dp[i][j]);
    		ans[i]=min(ans[i],dp[i][j]);//对答案取最小值 
		}
	}
    while(t--)
    {
    	cin>>s;
    	cout<<ans[s]<<endl;
	}
	return 0;
}
posted @ 2021-07-17 12:37  luckydrawbox  阅读(43)  评论(0)    收藏  举报  来源