牛客多校第一场C题Euclidean Distance水解

登录—专业IT笔试面试备考平台_牛客网

https://ac.nowcoder.com/acm/contest/881/C

题目链接:

https://ac.nowcoder.com/acm/contest/881/C

题目大意:

给定一个N维坐标系的点A(a1/m,a2/m,a3/m,...,an/m),寻找一个点P(p1,p2,p3,...,pn)满足p点的各坐标之和为1,且p1,p2,p3,...,pn > 0,使得A点到P点的欧几里得距离最小,其中A与P之间的欧几里得距离即为,求这个最小的欧几里得距离,若为分数则用分数形式表示。

思路:

首先将分母的m处理掉(记在分母),我们先将所有坐标放大m倍 = m , A(a1,a2,a3,...,an),接下里我们转换一下问题,首先对A的坐标从大到小排个序,然后我们来看一下这张图(先画八个吧后面自己脑补)图片说明
这是排序后的A的坐标们,我们现在需要确定P的坐标,也就是将-m(因为在欧几里得距离中A和P的坐标是直接相减关系)分配到这个图里,不难证明按照如下方法来分配效果最佳,图片说明
以下给出简答说明:我们要使得减去pi后的ai平方和最小,那么是ai中较大的数减小的收益一定比使较小的数(负的不就更小么,收益为负)变小的收益大,那么我们的第一次操作如图,将A1推平到与A2一样,为什么不继续往下推呢?因为我们接下来要连A2一起推!(否则A2也是较大不推血亏),同理,当我们的m剩余量还够时,我们就直接将A1,A2推平到A3,那么如图,加入当我们需要将A1,A2,A3,A4推平到A5时,我们的m不够用了,我们只需要将他们整体往下尽可能的推就行了,依然不难证明,参差不齐的推收益更小。
那么我们要做的就是维护一个前缀,并不断修改,前缀和m了,虽然这样很好懂,但其实这样,还是麻烦了!
上面说到了我们每次推平前i个是最优,这确实没错,但我们看看最后的状态,我们既然推平了前面的所有,那为何不一步推到位呢?我们只需要排序后记录前缀和,然后考虑到哪里前缀和减去m平不下去了,我们就推到哪里!
(这里应要求加上了样例3 10 1 -2 3 的情况,领会一下吧,图片说明
那么代码如下,欢迎大家提出意见:

#include <cstdio>
#include <algorithm>
#include <cstring>
#define Maxn 100008
using namespace std;
typedef long long ll;

ll n,m;
ll a[Maxn],sum[Maxn];

ll gcd(ll a,ll b){ return (!b) ? a : gcd(b,a%b);}

bool cmp(ll a,ll b){    return a > b;}

int main()
{
    while(scanf("%lld%lld",&n,&m) != EOF)
    {
        ll ansa,ansb,now = n;//now表示能够推平到的位置,ansa记录分子,ansb记录分母
        for(ll i = 1;i <= n;i ++ ) scanf("%lld",&a[i]);
        sort(a+1,a+n+1,cmp);
        sum[0] = -m;
        for(int i = 1;i <= n;i ++) sum[i] = sum[i - 1] + a[i];//前缀和
        for(int i = 1;i < n;i ++)
        {
            if(sum[i] > a[i+1] * i)
            {
                now = i;
                break;
            }
        }
        ansa = sum[now] * sum[now] * now;
        ansb = now * now;
        for(ll i = now + 1;i <= n;i ++)
                    ansa += a[i] * a[i] * ansb;
        ansb *= (m * m);
        ll gd = gcd(ansa,ansb);
        ansa /= gd,ansb /= gd;
        if(ansb == 1 || (!ansa)) printf("%lld\n",ansa);
        else printf("%lld/%lld\n",ansa,ansb);
    }
    return 0;
}

给没看懂代码中分子ansa = sum[now] * sum[now] * now;的处理的同学下面一个简单说明:我们既然推平了前now项,那么每一项为sum[now] / now,平方后为(sum[now]/now)^2,由于有now项,我们需要再乘一个now,于是分子就变成了sum[now] * sum[now] * now

全部评论
前缀和那一步我解释一下 a[0] + a[1] + a[2] + ... + a[i] - m > a[i + 1] * i 化简之后为 (a[0] - a[i + 1]) + (a[1] - a[i + 1]) + (a[2] - a[i + 1]) + ... + (a[i] - a[i + 1]) > m
点赞 回复 分享
发布于 2019-07-23 17:49
相当于是个拽力,计算平均能被拽下去的效果的话,必须满足平均拽的效果不会使变为负数如果分界线答案是只取到的话,就是要满足被拽为负数就行对吧?
点赞 回复 分享
发布于 2019-08-17 20:36
因为有人问,发条评论解释下吧:我们先是处理了分母暂时使p的坐标总和为m,又因为求解的是a和p的各坐标差的平方和,所以我们又看作在a的所有坐标中一共减去m,再求平方和
点赞 回复 分享
发布于 2019-07-19 15:47
大佬优秀啊
点赞 回复 分享
发布于 2019-07-19 11:19
大佬大佬,我的方法感觉和你很类似,但是我其实根本不明白为什么会AC,各种解释也很牵强,能帮我看看吗? https://www.cnblogs.com/zaq19970105/p/11218980.html
点赞 回复 分享
发布于 2019-07-20 21:26
大佬太秀了😄
点赞 回复 分享
发布于 2019-07-19 20:59
水题可还行🤣
点赞 回复 分享
发布于 2019-07-19 20:17
 ansa += a[i] * a[i] * ansb;也就是 ansa += a[i] * a[i] * i * i 这个我不理解
点赞 回复 分享
发布于 2019-07-19 17:42
如何证明这样平推一定可以取得最小值
点赞 回复 分享
发布于 2019-07-19 14:40
😘😘😘
点赞 回复 分享
发布于 2019-07-19 14:19

相关推荐

求问!考研下岸,打算参加春招,我这个bg能进啥厂,或者需要搞点深度项目再投吗
Java抽象带篮子_...:直接海投,可以看看我的考研失利速成冲春招贴,里面详细写了简历怎么写,学哪些项目可以速成
点赞 评论 收藏
分享
评论
15
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务