1.题目
G.爬山
编程题
小明这天在参加公司团建,团建项目是爬山。在 x 轴上从左到右一共有 n座山,第 i 座山的高度为 hi。他们需要从左到右依次爬过所有的山,需要花费的体力值为 S = Σni=1hi。
然而小明偷偷学了魔法,可以降低一些山的高度。他掌握两种魔法,第一种魔法可以将高度为 H 的山的高度变为 ⌊√H⌋,可以使用 P 次;第二种魔法可以将高度为 H 的山的高度变为 ⌊H/2⌋,可以使用 Q 次。并且对于每座山可以按任意顺序多次释放这两种魔法。
小明想合理规划在哪些山使用魔法,使得爬山花费的体力值最少。请问最优情况下需要花费的体力值是多少?
输入格式:
输入共两行。
第一行为三个整数 n,P,Q。
第二行为 n 个整数 h1,h2,. . . ,hn。
输出格式:
输出共一行,一个整数代表答案。
样例输入:
4 1 1
4 5 6 49
样例输出:
18
2.错误思路
错误思路不是不应该说,而是很有必要说说,因为只有了解到错误后才能更好改正。
我们应该第一反应是构造函数
那么我们来尝试一下,设开根号的减少量为f1(x),开二分之一的减少量为f2(x),那么很容易知道如下
f1(x)=x-√x
f2(x)=x-x/2=x/2
我们希望爬山的高度尽可能小,我们应该比较这两种方式谁的减少量更多于是我们令
f3(x)=f1(x)-f2(x)=x/2-√x
很明显我们不知道这个函数的单调性,要知道函数单调性就应该带考虑求导
f3'(x)=1/2-1/2√x
明显知道f3(x)在0—1上单调递减,在1—+∞单调递增,很明显当x=4时f3(4)=0,所以我们可以知道对于一个数当它大于等于4时我们应该考虑开根号性价比高,小于4时开二分之一性价比高。那么这个思想的代码就如下:
#include<bits/stdc++.h>
#include<iostream>
using namespace std;
int n,a[10],p,q;
int main()
{
cin>>n>>p>>q;
for(int i=0;i<n;i++)
{
cin>>a[i];
}
sort(a,a+n);
int P=p;
for(int i=0;i<p;i++)
{
if(a[n-1]<4)
{
break;
}
a[n-1]=sqrt(a[n-1]);
--P;
sort(a,a+n);
}
if(a[n-1]>=4)
{
for(int i=0;i<q;i++)
{
a[n-1]=a[n-1]/2;
sort(a,a+n);
}
}
if(a[n-1]<4)
{
for(int i=0;i<P;i++)
{
a[n-1]=sqrt(a[n-1]);
sort(a,a+n);
}
for(int i=0;i<q;i++)
{
a[n-1]=a[n-1]/2;
sort(a,a+n);
}
}
int sum=0;
for(int i=0;i<n;i++)
{
sum+=a[i];
}
cout<<sum;
return 0;
}
但是,我想告诉你的是,这是错误思路,其实我们想的一点都没有错,如果这里的山不是整数而是小数我们的想法完全正确因为我们刚才讨论的时候,高度X是连续的,而题目的X是不连续的,所以导致出现问题。
就比如
按照我们的想法应该是
f1(49)=√49=7
f2(48)=48/2=24
hight=f1+f2=31
看样子没问题啊,但是如果我们仔细一点就会发现问题,如果我们交换一下
f1(48)=√48=6
f2(49)=49/2=24
hight=f1+f2=30
所以这个思路有瑕疵,那就改进一下或者改变想法。
3.正确思路
通过观察可知
√49=7 √48=6
49/2=24 48/2=24
如果一个数是是一个完全平方数那么应该有
int a;
int t=sqrt(a);
t*t==a;
我们通过上面的式子可以知道一个数是不是完全平方数,现在我们考虑一下如何解决当一个数应该开平方还是开二分之一
对于一个数来说毋庸置疑是可以按照我们那个错误思想,但如果要考虑二个以上的数时就应该考虑整体性,比如
15 16 √15=3 √16=4 15/2=7 16/2=8 3+8=11 7+4=11
24 25 √24=4√25=5 24/2=12 25/2=12 4+12=16 12+5=17
35 36 √35=4√36=6 35/2=17 36/2=18 18+4=22 17+6=23
48 49 √48=6√49=7 48/2=24 49/2=24 24+6=30 7+24=31
通过举例可以知道如果一个数是完全平方并且比它小的数是该数减去1,则我们的第一个思想就不合适了。
那么正确的思路应该是我们先判断数组最大的数是否满足上面的要求,然后一直遍历最大值,对最大进行选择,然后排序。经历P+Q次后得到一个处理过的数组,让后累加结束。
代码如下
#include<bits/stdc++.h>
using namespace std;
int n, p, q;
int a[100];
int t, t1, t3;
bool chargemin_1(int a[],int n){
sort(a,a+n);
for(int i=0;i<n-1;i++){
if(a[n-1]==a[i]+1){
return true;
}
}
return false;
}
int main()
{
cin >> n >> p >> q;
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
int s = p + q+1;
sort(a,a+n);
while (--s)
{
t = 0;
t1 = 0;
t3 = 0;
t = a[n-1] - sqrt(a[n - 1]);//t是开根号的减少量
t1 = a[n-1] / 2;//t1是开二分之一的减少量
t3 = sqrt(a[n - 1]);//开根号判断是不是完全平方数
if (t3 * t3 == a[n - 1] && chargemin_1(a,n) && q > 0)//特殊情况
{
a[n - 1] = a[n - 1] / 2;
q--;
sort(a, a + n);
continue;
}
if (t <= t1 && q > 0)//一般情况
{
a[n - 1] = a[n - 1] / 2;
q--;
sort(a, a + n);
continue;
}
if (t >= t1 && p>0)//一般情况
{
a[n - 1] = sqrt(a[n - 1]);
p--;
sort(a, a + n);
continue;
}
if(p==0&&q>0)//特殊情况
{
int Q=q;
for(int i=0;i<Q;i++)
{
a[n - 1] = a[n - 1] / 2;
q--;
sort(a, a + n);
}
}
if(q==0&&p>0)//特殊情况
{
int P=p;
for(int i=0;i<P;i++)
{
a[n - 1] = sqrt(a[n - 1]);
p--;
sort(a, a + n);
}
}
}
int ans = 0;
for (int i = 0; i < n; i++)//统计
{
ans += a[i];
}
cout << ans;
return 0;
}
这就是我的思路,本人第一次写博客,有问题请指出来,本人必谢。
谢谢大家!