CF1550A Find The Array

题意

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

  • ai=1a_i=1

  • ai1a_i-1 在这个数列中。

  • ai2a_i-2 在这个数列中。

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

做法1:贪心

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

证明

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

  • 若溢出,设最后放入的数为 ana_n,此时与要求的元素和 ss 的差为 c=i=1naisc=\sum_{i=1}^na_i-s,可令 ananca_n\Rightarrow a_n-c,由我们之前的计算得,这时的 an1a_n≥1,无论 ana_n 为什么数,必然有一个 aia_i 满足 aian2\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 的美丽的数列最少需要几个元素。同时,设我们答案的美丽序列是按顺序排列的,即 a1a2ana_1≤a_2≤…≤a_n

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

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

其中 0jis0≤j≤i≤s

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

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

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

  • 前一个放了 j1j-1,此时再放一个 jjdpi,j=min(dpi,j,dpij,j1+1)dp_{i,j}=\min(dp_{i,j},dp_{i-j,j-1}+1),同上,需满足之前的和 ijj1i-j≥j-1

  • 前一个放了 j2j-2,此时再放一个 jjdpi,j=min(dpi,j,dpij,j2+1)dp_{i,j}=\min(dp_{i,j},dp_{i-j,j-2}+1),同上,需满足之前的和 ijj2i-j≥j-2

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

dpi,j=min{dpij,j+1ijjdpij,j1+1ijj1dpij,j2+1ijj2dp_{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}

其中 2jis2≤j≤i≤sj=1j=1 的情况前面已经计算过。

做完上面的计算后,我们用 ansans 数组记录答案,即 ansians_i 表示和为 ii 时的答案,显然 ansi=minj=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  阅读(11)  评论(0)    收藏  举报  来源