「SOL」工廠選址(BZOJ)

一如既往地咕spa


# 題面

某地區有 \(m\) 座煤礦,其中第 \(i\) 號礦每一年產量爲 \(a_i\) 噸。code

有一箇舊發電廠,每一年需用煤 \(b\) 噸(將剛好 \(b\) 噸煤運給舊發電廠),每一年維護費用爲 \(h\) 元,每噸原煤從第 \(i\) 號礦運到舊發電廠的運費爲 \(C_{i0}\)\(i=1,2,\cdots,m\))。rem

現規劃新建一個發電廠,\(m\) 座煤礦每一年開採的原煤除了供給舊發電廠的 \(b\) 噸,其他所有供給新發電廠。現有 \(n\) 個備選的廠址。若在第 \(j\) 號備選廠址建新廠,每一年維護費用爲 \(h_j\) 元。每噸原煤從第 \(i\) 號礦運到 \(j\) 號備選廠址的運費爲 \(C_{ij}\)\(i=1,2,\cdots,m\)\(j=1,2,\cdots,n\))。get

求總費用的最小值,以及在該最小費用下,新發電廠的廠址編號最小是多少。string

數據規模 \(n\le50\)\(m\le50000\)\(b\le10000\)it


# 解析

這道題能夠直接貪心,可是既然要學帶悔貪心,用帶悔貪心作也不是不行 awaio

\(n\) 不大,能夠對每一個廠址都計算一次「將新發電廠建在該處的最小花費」。因而只須要決策煤的分配模板

\(c_i\) 爲每噸煤從煤礦 \(i\) 運到舊發電廠的代價,\(c_i'\) 爲運到新發電廠的代價。class

先考慮簡單的狀況,假如每一個煤礦只有一噸煤。queue

由於舊發電廠必需要 \(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\n%lld\n", anspos, anscost);
	return 0;
}

THE END

Thanks for reading!

\[\begin{split} 「\ &誰かに気づいて欲しくて\\ &\quad\small{「\ 但願誰會注意到}\\ &ただ ただ 歩きまわるの\\ &\quad\small{僅僅,僅僅這樣前進着}\\ &そんな噓ばっか\\ &\quad\small{全是這樣的謊話}\\ &実らせていくけど\\ &\quad\small{可還要繼續說下去}\\ &君に見つけて欲しくて\ 」\\ &\quad\small{但願你能夠找尋到\ 」}\\ ——&\text{《餘命3日少女(Cover)》By 米白} \end{split} \]

> Link 餘命3日少女-網易雲

相關文章
相關標籤/搜索