贪心--牛客wannafly挑战赛7F

贪心--牛客wannafly挑战赛7F

链接:

https://ac.nowcoder.com/acm/contest/56/F

来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

有一天Masha回到家,发现有n只老鼠在它公寓的走廊上,她大声呼叫,所以老鼠们都跑进了走廊的洞中。
这个走廊可以用一个数轴来表示,上面有n只老鼠和m个老鼠洞。第i只老鼠有一个坐标𝑥𝑖,第𝑗个洞有一个坐标𝑝𝑗和容量𝑐𝑗。容量表示最多能容纳的老鼠数量。
找到让老鼠们全部都进洞的方式,使得所有老鼠运动距离总和最小。老鼠i进入洞j的运动距离为|𝑥𝑖 − 𝑝𝑗|
无解输出-1。

输入描述:

第一行包含两个整数n,m,表示老鼠和洞的数量。
第二行包含n个整数𝑥1...𝑛,表示老鼠坐标。
接下来m行每行两个整数𝑝, 𝑐,表示每个洞的坐标和容量。

输出描述:

输出最小运动距离总和或-1。

示例1

输入

复制

4 5 
6 2 8 9 
3 6 
2 1 
3 6 
4 7
4 7

输出

复制

11

示例2

输入

复制

7 2 
10 20 30 40 50 45 35 
-1000000000 10 
1000000000 1

输出

复制

7000000130

备注:

𝑛, 𝑚 ≤ 106, 1 ≤ 𝑐 ≤ 𝑛, 1 ≤ |𝑝|, |𝑥| ≤ 109

思路

根据贪心策略,每只老鼠有两个选择,左边最近的有容量的洞,右边最近的有容量的洞。

优先队列Q1存老鼠,Q2存洞,按坐标大小出队。存的值为-k-x,x为该点坐标,Q1为老鼠坐标,Q2为洞的坐标,k为偏差值。

洞:左边的老鼠进,贡献为x,右边的老鼠进,贡献为-x

老鼠:进左边的洞,贡献为x,进右边的洞,贡献为-x

将代码取消注释,输入最下方的洞-鼠-洞-鼠-洞样例,仔细观察。当鼠2进洞2时,鼠1的位置被挤掉,那么鼠1就会回到上一个状态,k值算的就是所有现状态回到上一个状态时的偏差值的累加,即ans += k,就能让ans回到上一个状态。具体观察洞2出现时的ans,和鼠2出现时的ans思考。

#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <iomanip>
#include <cstdio>

using namespace std;
typedef long long LL;
typedef pair<LL, LL> PLL;

const LL N = 2e6+50;

#define mk make_pair

PLL p[N];
LL n, m, k;
LL sum, ans;
priority_queue<LL, vector<LL>, greater<LL> > Q1;//老鼠,每只老鼠都push
priority_queue<PLL, vector<PLL>, greater<PLL> > Q2;//洞,每进一只老鼠push

LL read()
{
    LL ret = 0, t = 1;char c = getchar();
    while(c > '9' || c < '0') c = c == '-'?t=-1:getchar();
    while(c <= '9' && c >= '0') {ret = ret*10+c-'0';c = getchar();}
    return t*ret;
}

int main()
{
    n = read();m = read();
    sum = n;
    for(int i = 1;i <= n;++i)
        scanf("%lld", &p[i].first);
    for(int i = 1;i <= m;++i)
    {
        p[n+i].first = read();p[n+i].second = read();
        sum -= p[n+i].second;
    }
    n += m;
    if(sum > 0)
    {
        puts("-1");
        return 0;
    }
    sort(p+1, p+n+1);
    for(int i = 1;i <= n;++i)
    {
        int x = p[i].first;//不写会超时
        if(p[i].second)//洞
        {
            while(p[i].second && !Q1.empty() && x+Q1.top() < 0)//例洞鼠洞情况,!empty()表左边有可移动的老鼠,<0表左边老鼠到右边的洞比较近
            {
                k = x+Q1.top();
                ans += k;
                Q1.pop();
                --p[i].second;
                Q2.push(mk(-k-x, 0));//0表左边进来的老鼠无法被右边的老鼠挤掉,即该老鼠所处的洞的容量为0
            }
            if(p[i].second)//到i点洞比老鼠多,右边最近的老鼠过来,洞的贡献是-p[i].first
            {
                --p[i].second;
                Q2.push(mk(-x, i));
            }
        }
        else//老鼠
        {
            k = 1LL<<32;//老鼠都在左边,初始化为INF
            if(!Q2.empty())//老鼠找左边有容量的最近的洞
            {
                int j = Q2.top().second;
                k = x+Q2.top().first;
                Q2.pop();
                if(p[j].second)//该老鼠可以被挤掉且洞还有容量的话
                {
                    --p[j].second;
                    Q2.push(mk(-p[j].first, j));
                }
            }
            ans += k;
            Q1.push(-k-x);
        }
        cout << k << " " << x << " " << -k-x << " " << ans << endl;
    }
    printf("%lld\n", ans);
    return 0;
}
/*
2 3
3 7
1 1
4 1
8 1
*/

posted @ 2019-08-11 13:53  谁知道你啊啊啊  阅读(322)  评论(0编辑  收藏  举报