标签:老鼠 uoj455 w1 int 雪灾 w2 外卖 y1 y2
题面:uoj455
题意:有n只老鼠和m个洞,每个洞有容量限制和一个费用。要求每只老鼠进一个洞,使所有老鼠的移动距离和加进洞费用和最小。
题解:模拟费用流(贪心+反悔)
将所有的老鼠和洞按位置排序。考虑先让老鼠向左边的洞匹配。
维护老鼠和洞两个堆:M,H。老鼠移动的距离为右减左。
当前是老鼠:坐标为x,从洞堆H中取出最优解v,则代价为x+v,这样只考虑了向左匹配,但有可能最优解它向右匹配,因此考虑反悔。将这次匹配的代价消去,并将这个点作为左端点(即右边的洞向左匹配该老鼠),因此向老鼠堆M中加入−(x+v)−x。
当前是洞:坐标为y,从老鼠堆M中取出最优解v,代价为y+v+w。考虑反悔,即右边的老鼠匹配该洞,向洞堆H中加入−(y+v+w)−y+w。取消上一次的代价−(y+v+w),并把当前洞作为左端点−y,以及算上进洞的代价+w。
还有一种情况是该洞右边的洞匹配该洞原匹配老鼠,即 鼠 洞 洞 情况,应向老鼠堆M中加入−w−y。向老鼠堆加入是因为每次遇到洞都从老鼠堆M中找最小的。加入−w−y原因如下:
设两个洞的费用和坐标分别为w1,y1,w2,y2,老鼠堆M中取出的最优解为v。用右边的洞替换左边的洞的代价为(w2+y2+v)−(w1+y1+v)=w2+y2−w1−y1。当碰到右边洞的时候,计算的代价为y2+v+w2=y2−(w1+y1)+w2=w2+y2−w1−y1。
每次都暴力地插入两个堆,时间复杂度过高。因此可以在插入堆的时候记录一下次数,插入时整体插入,反悔可以分裂。
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0, f = 1; char c; c = getchar() ;
while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar() ;
return x * f ;
}
typedef long long ll ;
const int maxn = 1e5 + 10, INF = 0x3f3f3f3f ;
int n, m, x[maxn], y[maxn], w[maxn], c[maxn] ;
ll ans = 0 ;
struct data {
ll val; mutable int cnt ;
data () {}
data (ll a, int b) {val = a, cnt = b;}
bool operator < (const data &a) const {
return val > a.val ;
}
} ;
priority_queue<data> M, H ;
void insert_mouse (int x) {
ll cost = INF ;
if (!H.empty()) {
cost = H.top().val + x ;
if (!(-- H.top().cnt)) H.pop() ;
}
ans += cost ;
M.push (data (-cost - x, 1)) ; //右边的洞匹配该老鼠
}
void insert_hole (int y, int w, int c) {
int tot = 0 ;
while (!M.empty() && tot < c) {
ll cost = M.top().val + w + y ;
if (cost >= 0) break ;
int cnt = min (M.top().cnt, c - tot) ;
ans += cost * cnt ;
tot += cnt ;
H.push (data (-cost - y + w, cnt)) ; //右边的老鼠匹配该洞
if (!(M.top().cnt -= cnt)) M.pop() ;
}
if (tot) M.push (data (-w - y, tot)) ; //右边的洞匹配该洞
if (c - tot) H.push (data (-y + w, c - tot)) ; //洞有剩余容量,分裂
}
int main() {
n = read(), m = read(); ll sum = 0 ;
for (int i = 1; i <= n; i ++) x[i] = read() ;
for (int i = 1; i <= m; i ++) {
y[i] = read(); w[i] = read(); c[i] = read() ;
sum += c[i] ;
}
if (sum < n) {
printf("-1\n"); return 0 ;
}
for (int i = 1, j = 1; i <= n || j <= m;) {
if (i <= n && (j > m || x[i] < y[j])) { //按x顺序插入
insert_mouse (x[i]); i ++ ;
} else {
insert_hole (y[j], w[j], c[j]); j ++ ;
}
}
printf("%lld\n", ans) ;
return 0 ;
}
标签:老鼠,uoj455,w1,int,雪灾,w2,外卖,y1,y2 来源: https://blog.csdn.net/wennitao222/article/details/91399920
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。