• 「SOL」工厂选址(BZOJ)


    一如既往地咕


    # 题面

    某地区有 (m) 座煤矿,其中第 (i) 号矿每年产量为 (a_i) 吨。

    有一个旧发电厂,每年需用煤 (b) 吨(将恰好 (b) 吨煤运给旧发电厂),每年维护费用为 (h) 元,每吨原煤从第 (i) 号矿运到旧发电厂的运费为 (C_{i0})(i=1,2,cdots,m))。

    现规划新建一个发电厂,(m) 座煤矿每年开采的原煤除了供给旧发电厂的 (b) 吨,其余全部供给新发电厂。现有 (n) 个备选的厂址。若在第 (j) 号备选厂址建新厂,每年维护费用为 (h_j) 元。每吨原煤从第 (i) 号矿运到 (j) 号备选厂址的运费为 (C_{ij})(i=1,2,cdots,m)(j=1,2,cdots,n))。

    求总费用的最小值,以及在该最小费用下,新发电厂的厂址编号最小是多少。

    数据规模 (nle50)(mle50000)(ble10000)


    # 解析

    这道题可以直接贪心,但是既然要学带悔贪心,用带悔贪心做也不是不行 awa

    (n) 不大,可以对每个厂址都计算一次“将新发电厂建在该处的最小花费”。于是只需要决策煤的分配

    (c_i) 为每吨煤从煤矿 (i) 运到旧发电厂的代价,(c_i') 为运到新发电厂的代价。

    先考虑简单的情况,假如每个煤矿只有一吨煤。

    因为旧发电厂必须要 (b) 吨煤,不妨先随便安排把这 (b) 吨凑满。

    然后剩下的煤直接分配给新发电厂吗?显然是不一定最优的——考虑当前煤矿 (i) 的一吨煤:

    • 如果煤矿 (j) 的一吨煤供给了旧发电厂;
    • 而用煤矿 (i) 替代煤矿 (j) 去供给旧发电厂是否会更优?

    条件就是 (c_i+c_j'< c_i'+c_j) 等价于

    [c_i-c_i'< c_j-c_j' ]

    于是维护当前供给旧发电厂的煤矿的 (c_j-c_j')大根堆,如果堆顶可以被煤矿 (i) 替代,则替代。由于替代出的 (j) 是当前堆中最大的,所以 (j) 不可能反过来又替代掉堆中其他的煤矿。

    但是现在煤矿不止有一吨煤怎么办?于是我们联想到带悔贪心(模拟费用流)的模板题中“每个洞可以容纳多只老鼠”的情况,用堆维护 pair ,维护 (c_j-c_j') 的大根堆,以及该类煤有多少吨。

    详见代码。


    # 源代码

    /*Lucky_Glass*/
    #include<queue>
    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    inline int Rint(int &r){
    	int b=1, c=getchar(); r=0;
    	while(c<'0' || '9'<c) b=c=='-'?-1:b, c=getchar();
    	while('0'<=c && c<='9') r=(r<<1)+(r<<3)+(c^'0'), c=getchar();
    	return r*=b;
    }
    const int N=55, M=5e4+10;
    typedef pair<int, int> pii;
    
    int m, varB, varH, n;
    int num[M], aryh[N], aryc[M][2];
    
    int main(){
    	Rint(m), Rint(varB), Rint(varH), Rint(n);
    	for(int i=1; i<=m; i++) Rint(num[i]);
    	for(int i=1; i<=n; i++) Rint(aryh[i]);
    	for(int i=1; i<=m; i++) Rint(aryc[i][0]);
    	long long anscost=0;
    	int anspos=-1;
    	for(int i=1; i<=n; i++){ //把新工厂建在 i 位置
    		for(int j=1; j<=m; j++) Rint(aryc[j][1]);
    		long long total=varH+aryh[i];
    		int rem=varB;
    		//to new - to old
    		priority_queue<pii, vector<pii>, greater<pii> > que;
    		for(int j=1; j<=m; j++){
    			//tmp: 工厂j还剩多少单位
    			//use: 工厂j运给旧工厂多少单位
    			int tmp=num[j], use=0;
    			if(rem) //旧工厂不够
    				if(rem>=tmp){ //全部运给旧工厂
    					total+=1ll*aryc[j][0]*tmp;
    					rem-=tmp, use=tmp, tmp=0;
    				}
    				else{ //还有一些剩余
    					total+=1ll*aryc[j][0]*rem;
    					tmp-=rem, use=rem, rem=0;
    				}
    			while(!que.empty() && tmp){ //尝试用 j 代替原本供应给旧工厂的 top
    				if(que.top().first>=aryc[j][1]-aryc[j][0]) break;
    				pii tp=que.top(); que.pop();
    				int now=min(tmp, tp.second); //替代了 now 单位
    				use+=now, tp.second-=now, tmp-=now;
    				if(tp.second) que.push(tp);
    				total+=1ll*now*tp.first+1ll*now*aryc[j][0];
    			}
    			if(tmp) total+=1ll*tmp*aryc[j][1]; //其他剩余全部给新工厂
    			if(use) que.push(make_pair(aryc[j][1]-aryc[j][0], use)); //运给旧工厂的 use 单位准备反悔
    		}
    		if(~anspos){
    			if(total<anscost)
    				anscost=total, anspos=i;
    		}
    		else anscost=total, anspos=i;
    	}
    	printf("%d
    %lld
    ", anspos, anscost);
    	return 0;
    }
    

    THE END

    Thanks for reading!

    [egin{split} “ &誰かに気づいて欲しくて\ &quadsmall{“ 希望谁会注意到}\ &ただ ただ 歩きまわるの\ &quadsmall{仅仅,仅仅这样前进着}\ &そんな嘘ばっか\ &quadsmall{全是这样的谎言}\ &実らせていくけど\ &quadsmall{可还要继续说下去}\ &君に見つけて欲しくて ”\ &quadsmall{希望你可以找寻到 ”}\ ——& ext{《余命3日少女(Cover)》By 米白} end{split} ]

    > Link 余命3日少女-网易云

    欢迎转载٩(๑❛ᴗ❛๑)۶,请在转载文章末尾附上原博文网址~
  • 相关阅读:
    opencv实现连通域
    C Tips:显示点阵汉字的小样例
    协方差的意义
    HashTable类模板_C++
    Java实现 蓝桥杯VIP 算法提高 选择排序
    Java实现 蓝桥杯VIP 算法提高 笨小猴
    Java实现 蓝桥杯VIP 算法提高 笨小猴
    Java实现 蓝桥杯VIP 算法提高 笨小猴
    Java实现 蓝桥杯VIP 算法提高 笨小猴
    Java实现 蓝桥杯VIP 算法提高 笨小猴
  • 原文地址:https://www.cnblogs.com/LuckyGlass-blog/p/14360162.html
Copyright © 2020-2023  润新知