CF1550A Find The Array
题意
我们称一个数列 是美丽的,当且仅当数列中每个元素 至少满足如下三个条件之一:
-
。
-
在这个数列中。
-
在这个数列中。
现在给你一个整数 ,求出一个元素和为 的美丽的数列至少要几个元素?
做法1:贪心
要求最少的元素个数,我们就让放入的数尽量大,即每次都放比前面的数大 的数,直到放满或溢出为止。
证明
-
若放满,自然满足题目要求。
-
若溢出,设最后放入的数为 ,此时与要求的元素和 的差为 ,可令 ,由我们之前的计算得,这时的 ,无论 为什么数,必然有一个 满足 ,也满足了题意。
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:动态规划
设 表示一个和为 ,最后放的数为 的美丽的数列最少需要几个元素。同时,设我们答案的美丽序列是按顺序排列的,即 。
我们提前要做一下处理,即
其中 。
也就是我们提前放好 ,后面只需要处理一般情况,因为要取最小值,所以其他的设为无穷大。
接下来对于 ,我们有三种选择:
-
前一个放了 ,此时再放一个 :,因为之前放了 ,所以需满足之前的和 。
-
前一个放了 ,此时再放一个 :,同上,需满足之前的和 。
-
前一个放了 ,此时再放一个 :,同上,需满足之前的和 。
于是可以得出状态转移方程:
其中 , 的情况前面已经计算过。
做完上面的计算后,我们用 数组记录答案,即 表示和为 时的答案,显然 。
最后询问时,直接输出答案即可。总体时间复杂度为 。
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;
}

浙公网安备 33010602011771号