zoukankan      html  css  js  c++  java
  • 最普通的一类老鼠进洞问题

    看了一个下午的WC2019的模拟费用流的课件,感觉什么都不会……


    最普通的一类老鼠进洞问题:

    (n)只老鼠,坐标分别为(x_i);有(m)个洞,有坐标(y_i),代价(w_i),容量(c_i)。老鼠(i)进洞(j)的代价为(|x_i-y_i|+w_i)。每只老鼠必须进洞。

    板子题:https://uoj.ac/problem/455

    显然有性质:老鼠和洞的匹配不能相交。(也就是说对于任意(x_i<x_j),一定有(p_ile p_j)(p)表示匹配对象)

    考虑按照坐标从左往右加入老鼠和洞。对老鼠和洞分别维护堆(Q0)(Q1)。为了实现方便,提前加上代价为无穷的洞。

    如果加入老鼠(x)

    (Q1)中取出代价最少的,设代价为(v)

    则答案加上(x+v)

    因为老鼠可能会反悔,所以在(Q0)中加入(-(x+v)-x),表示匹配后面的洞的代价。

    如果洞反悔,由于老鼠必须进洞,它应该要往后再找一个洞,此时匹配相交。

    如果加入洞((y,w,c))

    在容量被耗尽或者不优(即匹配老鼠代价不为负数)前,重复从(Q0)中取出代价最少的老鼠,设其代价为(v)

    答案加上(w+y+v)

    因为老鼠可能会反悔选更后面的,所以在(Q0)加入(-(w+y+v)+v)表示匹配后面的代价。

    因为洞可能反悔匹配后面的老鼠,所以在(Q1)加入(-(w+y+v)+w-y)表示匹配后面的代价。

    实际上两种操作在后面不可能都发生,否则匹配相交。并且也不需要考虑进行反悔操作之后又有老鼠或洞重新回来匹配。

    退出循环,把剩下的容量个洞加入(Q1)

    因为这里老鼠可能会反悔多次,一个个加不优。注意到此时加的代价一样,就可以记下同样代价的数量一起处理。

    具体实现见代码:

    using namespace std;
    #include <bits/stdc++.h>
    #define N 100005
    #define ll long long
    #define INF 2000000000
    int n,m;
    ll X[N];
    ll Y[N],W[N],C[N];
    ll ans;
    struct info{ll v,cnt;};
    bool operator<(info a,info b){return b.v<a.v;}
    priority_queue<info> q0,q1;
    void ins0(ll x){
    	info t=q1.top();
    	q1.pop();
    	ans+=x+t.v;
    	if (--t.cnt)
    		q1.push(t);
    	q0.push((info){-(x+t.v)-x,1});
    }
    void ins1(ll x,ll w,ll c){
    	ll s=0;
    	while (c && !q0.empty()){
    		info t=q0.top();
    		if (w+x+t.v>=0)
    			break;
    		q0.pop();
    		ll mn=min(t.cnt,c);
    		ans+=(w+x+t.v)*mn;
    		q1.push((info){-(w+x+t.v)-x+w,mn});
    		s+=mn;
    		t.cnt-=mn;
    		c-=mn;
    		if (t.cnt)
    			q0.push(t);
    	}
    	if (c) q1.push((info){-x+w,c});
    	if (s) q0.push((info){-(w+x/*+t.v*/)/*+t.v*/,s});
    }
    int main(){
    //	freopen("in.txt","r",stdin);
    //	freopen("out.txt","w",stdout);
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n;++i) scanf("%lld",&X[i]);
    	ll sum=0;
    	for (int i=1;i<=m;++i) scanf("%lld%lld%lld",&Y[i],&W[i],&C[i]),sum+=C[i];
    	if (sum<n){
    		printf("-1
    ");
    		return 0;
    	}
    	q1.push((info){INF,INF});
    	int i=1,j=1;
    	while (i<=n || j<=m)
    		if (i<=n && (j>m || X[i]<Y[j]))
    			ins0(X[i]),i++;
    		else
    			ins1(Y[j],W[j],C[j]),j++;
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Message高级特性 & 内嵌Jetty实现文件服务器
    springboot中使用kindeditor富文本编辑器实现博客功能&vue-elementui使用vue-kindeditor
    Embarcadero RAD Studio XE5
    经典营销故事
    百度竞价教程 借助百度热力图让你的效果翻10倍
    无本借力:他是如何实现年收入70万?
    不用软件快速拥有几百个QQ群并都是管理员
    质保、保修、包修:含义不同
    域名反向解析在自建邮件群发服务器中的应用
    2014年1月1日,马年
  • 原文地址:https://www.cnblogs.com/jz-597/p/14326929.html
Copyright © 2011-2022 走看看