题目 [ 传送门 ]
题目描述
作为当代建筑的爱好者,Farmer John 建造了一个圆形新谷仓,谷仓内部 个房间排成环形,按顺时针顺序编号为,每个房间都有通往与其相邻的左右房间的门,还有一扇门通往外面。
现在 FJ 有 n 头奶牛,他的目标是让每个房间恰好有一头奶牛。然而不幸的是,现在奶牛们随意呆在某个房间里,第 i 个房间里有 头奶牛。保证 。
FJ 决定采用这样的方法来解决这个问题:让某些奶牛顺时针穿过某些房间到达指定的位置。如果一头奶牛穿过了 扇门,他消耗的能量为 。你需要帮 FJ 算出所有奶牛消耗的能量和最小值是多少。
输入格式
第一行一个整数 ,接下来 行,第 行一个整数 。
输出格式
输出所有奶牛最小消耗能量和。
分析
有一个比较明显的贪心,设a,b,c为3个位置,,那么a->b,b->c一定比a->c优,
简单的证明一下:
设a,b间距离为x, b,c间距离为y
则a->b,b->c 的 ,a->c的
显然,
那么,我们只需要确定了起点,就可以直接计算答案。
于是,现在的问题就在于怎么求起点(起点的定义为以该点出发,最后在不回到该点的情况下,可以完成交换)。 不难发现,起点的一定为0(感性理解一下),并且任意一个点到起点的总个数应该小于等于当前已经经过的位置数(否则多余的牛一定会对起点前的位置产生影响)。通过这2点就可以找到起点。 于是:
Code
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,s,id,ans,a[100005],b[100005],c[100005],t,l;
int main()
{
scanf("%lld",&n); id=n;
for(int i=1;i<=n;i++) scanf("%lld",a+i);
for(int i=n;i>=1;i--)
{
s+=a[i]; if(s>(id-i)) id=i-1,s=0;
if(a[id]!=0 && a[i]==0) id=i;
}
while(a[id]!=0) id--;//找起点
for(int i=id;i>=1;i--) b[++t]=a[i];
for(int i=n;i>id;i--) b[++t]=a[i]; t=0;
//把起点作为第一位,重构数组
for(int i=1;i<=n;i++)
{
if(b[i]==0) c[++t]=i;//c[]数组表示没有牛的位置
else
{
int cnt=0;
for(int j=1;j<=b[i] && l<t;j++) l++,ans+=(i-c[l])*(i-c[l]),cnt++;
if(cnt==b[i]) c[++t]=i;
//每次都走到第一个位置一定最优
}
}
cout<<ans;
return 0;
}