钱仓 (思维+优先队列或栈)

题目描述

Mike家有n个钱仓,他们构成一个环,从1到n顺时针方向分布,也就是第i个钱仓会和第i-1个和第i+1个相邻,特别地,第n个钱仓和第1个钱仓相邻。
众所周知,Mike是个极其聪明的人,所以,他不会把钱全部放在同一个钱仓,它会平均分配,每个钱仓放1 mol的钱。
在开始时,每个钱仓会有ci mol的钱,保证Σci=n,Mike会开着他的卡车将钱从一个钱仓顺时针运到一个钱仓。由于是小型卡车,每次只能运1 mol的钱,而且Mike要求每mol钱最多只能运一次。
如果这mol钱被运输且运输距离为d,那么Mike要付出d2的费用,问Mike付出的费用最少是多少。

输入
第一行包含一个整数n。
接下来n行,描述ci。

输出
仅包含一个整数,表示Mike最小付出的费用。

样例输入
10
1
0
0
2
0
0
1
2
2
2
样例输出
33

数据范围和时空限制
对于100%的数据,n≤100000,Σci=n。
时间限制: 1 Sec 内存限制: 128 MB

思路:

先把这个环变成一个长度为2*n的链。然后就是找到一个长度为n的链,使得在这条链上所有的点都可以单向移动,然后就可以用优先队列或者栈维护出答案,符合条件的哪条链要保证哪条链从左向右走所有前缀和都要>=0,然后我是用维护前缀和找的符合条件的前端点,详细过程见代码。

**这个题很早之前写的,最近整理时又看了一遍,怎么都想不起来以前的思路QAQ,又想了好久。。。以后写题一定要写注释。 **

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+15;
int a[N],s[N];
priority_queue<int>q1;
priority_queue<int,vector<int>,less<int> >q;
int main()
{
    int n,b;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];  a[i]-=1;
        a[i+n]=a[i],a[i+2*n]=a[i];
    }
    for(int i=1;i<=n;i++)
        s[i]=s[i-1]+a[i];
    int k=1,minz=s[1];
    for(int i=2;i<=n;i++)
    {
        //cout<<minz<<" "<<s[i]<<endl;
        if(minz>=s[i])
        {
            minz=s[i];k=i;
        }
    }
    //k-=1;
    if(k<n)
        k+=n;
    //cout<<"!!"<<k<<endl;
    for(int i=k+1;i<=k+n;i++)
    {

        b=a[i]+1;
        while(b)
        {
            q.push(i);
            b--;
        }
    }
    //cout<<k<<endl;
    long long ls=0;
    for(int i=k+n;i>=k+1;i--)
    {
        b=q.top();  q.pop();
        ls+=abs(i-b)*abs(i-b);
        //cout<<i<<" "<<b<<endl;
    }
    cout<<ls<<endl;
    return 0;
}

发布了19 篇原创文章 · 获赞 7 · 访问量 1110

猜你喜欢

转载自blog.csdn.net/qq_43559193/article/details/105342114
今日推荐