天天看點

貪心2題微軟面試題

微軟面試題

有n個小朋友坐成一圈,每人有a[i]個糖果。

每人隻能給左右兩人傳遞糖果。

每人每次傳遞一個糖果代價為1。

求使所有人獲得均等糖果的最小代價。

輸入格式

第一行輸入一個正整數n,表示小朋友的個數。

接下來n行,每行一個整數a[i],表示第i個小朋友初始得到的糖果的顆數。

輸出格式

輸出一個整數,表示最小代價。

資料範圍

1≤n≤10000001≤n≤1000000

資料保證一定有解。

輸入樣例:

4
1
2
5
4
           

輸出樣例:

4
           
貪心2題微軟面試題

說明:

其中x為傳遞糖果所需要用的代價數,

若x>0,則說明傳遞的方向是從ai->a[i-1];

若x<0,那麼傳遞的方向是從a[i-1]>ai;

分析題目:目标+限制

目标:要使得代價和最小。

也就是使得|x1|+|x2|+…|xn|最小

限制:使得a[i]=ave(a);//ave(a)也就是平均值

這是一個多元代數式,求最小值,應該想辦法轉化為1元代數式。

我們嘗試找出關系式:

對于每個a[i]:

_a表示a的平均值

a[1]-_a=x2-x1

a[2]-_a=x3-x2

a[n-2]-_a=x[n-1]-x[n-2]

a[n-1]-_a=x[n]-x[n-1]

a[n]-_a=x[1]-x[n]

整理為a一邊,x一邊:

x[n]=x[1]+_a-a[n];

x[n-1]=x[n]+_a-a[n-1]

帶入x[n]:其實尾部兩式直接相加即可得到x[n-1]

x[n-1]-x1=2_a-a[n]-a[n-1];

x[n-2]=x[n-1]-x[n-2]+_a;

尾部三式相加整理:

x(n-2)-x1=3_a-a[n]-a[n-1]-a[n-2];

又上面式子:

x[n]=x[1]+(_a-a[n]);

x[n-1]=x[1]+(2_a-a[n]-a[n-1]);

x(n-2)=x[1]+(3_a-a[n]-a[n-1]-a[n-2]);

xi=x[1]+【(n+1-i)_a-a[n]-a[n-1]-a[n-2]-…a[i]】;

特别的:

當i=1時候:

由于n_a=(a[1]+a[2]+…a[n])

是以:x[1]=x[1]+0;

可以總結每一項都可以由x1表示

而且可以轉化為xi=x1+ci的形式:

又c[n]=_a - a[n]

c[n-1]=2_a - a[n]-a[n-1]

c[n-2]=3_a - a[n]- a[n-1] -a[n-2]

是以:會發現每一項都比前一項多了一個_a再多減去一個a[n]

c[i]=c[i+1]+_a-a[i];

注意c[1]=0;(x1=x1+0)

進而将|x1|+|x2|+|x3|+…|xn|的最小化問題轉化為|x1+c1|+|x1+c2|+…|x1+cn|的坐标模型:

也就是坐标軸上的點

c1,c2,c3…cn到x1的最小值:

而由幾何意義不難想到:

x1在中位數的時候取得最小值:

即x1=c[i+1/2];(i從1開始,如果i從0開始,x1=c[n/2]);

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1000010;
typedef long long LL;
int n;
int a[N];
LL c[N];
int main()
{

scanf("%d",&n);
LL sum=0;
for(int i=1;i<=n;i++) {
    scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++){
    sum+=a[i];
}
LL ave=sum/n;
for(int i=n;i>1;i--){
    c[i]=c[i+1]+ave-a[i];
}
c[1]=0;
sort(c+1,c+n+1);
LL res=0;

for(int i=1;i<=n;i++){
    res+=abs(c[i]-c[(n+1)/2]);
}
printf("%lld\n",res);
    return 0;
}