洛谷P1368 均分纸牌(加强版)

P1368 均分纸牌(加强版)

题目描述

有 N 堆纸牌,编号分别为 1,2,…, N。每堆上有若干张,纸牌总数必为 N 的倍数。可以在任一堆上取1张纸牌,然后移动。

移牌规则为:在编号为 1 堆上取的纸牌,能移到编号为 2和N 的堆上;在编号为 N 的堆上取的纸牌,能移到编号为 N-1和1 的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。

现在要求找出一种移动方法,使每堆上纸牌数都一样多且牌的移动次数尽量少。

输入输出格式

输入格式:

第一行一个整数n

第二行为n个空格分开的正整数,为n堆纸牌的牌数。

输出格式:

只有一个数,为最少的移动次数。

输入输出样例

输入样例#1:
4
1 2 5 4
输出样例#1:
4

说明

对样例的说明:

①第4堆移动1张牌至第1堆

②第3堆移动1张牌至第2堆

③第3堆移动1张牌至第2堆

④第2堆移动1张牌至第1堆

此时移动次数为4最小

【数据范围】

对于40%的数据,n<=10000

对于100%的数据,n<=1000000,所有纸牌数总和在2147483647内

/*
    设平均数为xba
    
    不妨设a1给了an x1 张纸牌(k可正可负),a2给了a1 x2张纸牌, a3给了a2 x3 张纸牌……an给了a(n - 1) xn张纸牌,不难发现以下方程:
    
    xba = a1 - x1 + x2
    xba = a2 - x2 + x3
    xba = a3 - x3 + x4
    xba = a4 - x4 + x5
    ......
    xba = a(n - 1) - x(n - 1) + xn
    xba = an - xn + x1
    
    我们考虑最终结果,应该是
    |x1| + |x2| + |x3| + .... + |xn|
    
    换元法,得到
    ans = |x1| + |xba - a1 + x1| + |2xba -a1 - a2 + x1| + |3xba -a1 - a2 - a3 + x1| + ..... + |(n - 1)xba - Σai,i <= n - 1|转换为一次函数带绝对值的最值问题
    
    数形结合思想,看做是数轴上点的距离。
    中位数时距离和最小。
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define maxn 1000010
#define INF 0x3f3f3f3f
long long a[maxn],sum,f[maxn],xba,n,k,ans;
int main(){
    scanf("%d",&n);
    for(long long i=1;i<=n;i++){
        cin>>a[i];
        sum+=a[i];
    }
    xba=sum/n;
    f[1]=0;
    for(long long i=2;i<=n;i++)
        f[i]=f[i-1]+xba-a[i-1];
    sort(f+1,f+n+1);
    k=f[n/2+1];
    for(long long i=1;i<=n;i++)
        ans+=abs(k-f[i]);
    cout<<ans;
    return 0;
}
原文地址:https://www.cnblogs.com/thmyl/p/7429529.html