AcWing1536. 均分纸牌 && AcWing122. 糖果传递—数学推导、贪心

均分纸牌 && 糖果传递

  • 均分纸牌
  • 糖果传递

均分纸牌

题目链接 AcWing1536. 均分纸牌
问题描述
AcWing1536. 均分纸牌 && AcWing122. 糖果传递—数学推导、贪心_第1张图片
分析

这道题有个特殊的地方就是A1只能从A2获取纸牌,或者A1只能将多余的纸牌给A2,此操作后A1的纸牌数应该为avg。
A2的纸牌只能从A3获取,或者给A3不能从A1获取,或者给A1,因为A1==avg,这样做会使得操作重复(A1就是通过A2来达到avg的),这样我们就能知道当前的数Ai要通过Ai+1来达到avg。这里我们从左端A1来考虑,从右端An来考虑也是可以的。

代码如下

#include
#include
#include
#include
using namespace std;
const int N=110;
int f[N];
int res=0;

int main(){
    int n,sm=0;
    cin>>n;
    for(int i=1;i<=n;i++){
       scanf("%d",&f[i]);
       sm+=f[i];
    } 
    int avg=sm/n;
    for(int i=1;i<=n;i++)
        if(f[i]!=avg){
            res++;
            f[i+1]+=f[i]-avg;
        }
    cout<<res;
    return 0;
}

糖果传递

问题链接 AcWing122. 糖果传递
问题描述
AcWing1536. 均分纸牌 && AcWing122. 糖果传递—数学推导、贪心_第2张图片
分析
这道题看起来和上面的题很像,其实题意确实差不多,区别就是上一道题的两端和这道题的两端不同,这道题是一道环型均分纸牌问题,虽然改动了一点,但是解法却完全不同。
AcWing1536. 均分纸牌 && AcWing122. 糖果传递—数学推导、贪心_第3张图片
如图所示,a1可以从a2获得x2,a2可以从a3获得x3,… ,an从a1获得x1,其中x2,x3,…,x1可正可负。
我们可以预先得到均分后的糖果数量 avg=(a1+a2+…+an)/n
那么就有

a1+x2-x1=avg
a2+x3-x2=avg
a3+x4-x3=avg
.....
an-1 + xn -xn-1=avg
an +x1-xn=avg

变形后可得

x1-x2=a1-avg
x2-x3=a2-avg
....
xn-1 - xn=an-1 - avg
xn-x1 = an-avg

依次从下往上做累加可得

0=0
x2-x1=(an+an-1+..+a2)-(n-1)*avg
....
xn-1 -x1=(an-1 +an)-2*avg
xn-x1=an-avg

再将x1挪到右边,让x2~xn由x1来表示

x1=x1
x2=x1+(an+an-1+..+a2)-(n-1)*avg
....
xn-1=x1+(an-1 +an)-2*avg
xn=x1+an-avg

再变形,将等式右边转化成差的形式

x1=x1-0
x2=x1-{(n-1)*avg-(an+an-1+..+a2)}
....
xn-1=x1-{2*avg-(an-1 +an)}
xn=x1-{avg-an}

我们的目标是求最小的代价,也就是求最小的|x1|+|x2|+…+|xn|
那么根据上式,

min{|x1|+|x2|+....+|xn|}=min{|x1-0|+|x1-{(n-1)*avg-(an+an-1+..+a2)}|+.....+|x1-{2*avg-(an-1 +an)}|+|x1-{avg-an}|}

其中

min{|x1-0|+|x1-{(n-1)*avg-(an+an-1+..+a2)}|+.....+|x1-{2*avg-(an-1 +an)}|+|x1-{avg-an}|}

是不是看起来很熟悉?这个就是找到一个点x1,让x1到点0,(n-1)avg-(an+an-1+…+a2),…{2avg-(an-1 +an)},{avg-an}的距离和最小,那么这就相当于是一个区间选址问题,那这就很容易解决了。

总结,这道题偏数学,如果第一次遇到很难想出来这种解法

代码如下

#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=1e6+10;

ll f[N];
int h[N];
int main(){
    ll n,avg=0,res=0;
    cin>>n;
    for(int i=0;i<n;i++){
        scanf("%d",&h[i]);
        avg+=h[i];
    }
    avg/=n;
    ll t=0;
    for(int i=0;i<n;i++){
        t+=avg-h[n-i-1];
        f[i]=t;
    }
    sort(f,f+n);
    for(int i=0;i<n;i++)
        res+=abs(f[n>>1]-f[i]);
    cout<<res;
    return 0;
}

你可能感兴趣的:(排序,日常训练,数学推导,算法,c++)