链接:
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;//老鼠 priority_queue<PLL, vector<PLL>, greater<PLL> > Q2;//洞 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)); } 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 */