P4480 「BJWC2018」「網絡流與線性規划24題」餐巾計划問題


刷了n次用了奇淫技巧才拿到rk1,亥

這道題是網絡流二十四題中「餐巾計划問題」的加強版。

於是懷着試一試的心情用費用流交了一發:

哇塞,過了9個點!(強烈譴責出題人用*造數據

下面是費用流解法簡述:

那么我們把每一天拆為早上和晚上兩個點,設為 \(day_i,night_i\)

首先可以人為地規定一點:對一塊餐巾,我們要么在其臟了之后立即送洗,要么買一塊新干凈餐巾來代替它。

然后對於每一個操作,我們可以如下連邊:

對於買新干凈餐巾:我們可以視作從源點買餐巾,從 \(s\)\(day_i\) 連邊即可

對於送慢洗部:從 \(night_i\)\(day_{i+n}\) 連邊,流量為 \(inf\) ,費用為 \(f\)

對於送快洗部:從 \(night_i\)\(day_{i+m}\) 連邊,流量為 \(inf\) ,費用為 \(s\)

但是我們注意到,可能存在某一天的干凈餐巾冗余。

於是我們要從 \(day_i\)\(day_{i+1}\) 連一條費用為零,流量為 \(inf\) 的單向邊。

如何保證每天剛好只用 \(r_i\) 塊餐巾?

將其拆成兩個板塊:

\(day_i\)\(t\) 連邊,流量為 \(r_i\),費用為 \(0\)

\(s\)\(night_i\) 連邊,流量為 \(r_i\),費用為 \(0\)


優化無果,於是猜想可以不用費用流。

首先無論餐巾是最開始一起買還是需要用時再買,不會影響最終的答案。

那么如果我們已經確定了要買的新餐巾的張數,那么是否可以確定一種唯一的方案使得總花費最小呢?

不難得到有這樣一種貪心策略:

在能夠用新餐巾的時候,盡量使用新餐巾。

\(m\) 為慢洗的天數。

如果無新餐巾可用,則倒回到 \(m\) 天前,找用過的舊餐巾進行慢洗。

如果沒有 \(m\) 天以前的舊餐巾可用,則由時間線從近到遠地找舊餐巾進行快洗。

這樣做使得時間線較遠的舊餐巾更有可能慢洗。

如果慢洗比快洗貴,那么直接將慢洗的時間和價格都改為快洗的時間和價格即可。

朴素代碼無O2只能過掉70分。

於是我懷疑單次判斷的時間復雜度過高。有的題解里說是 \(O(n)\) 的,但我死活沒看出來。

於是我們可以優化常數。

注意到在如果在第 \(i\) 天需要慢洗,所有在第 \(i-m\) 天前的舊餐巾對於我們來說是等效的,因為我們顯然沒有辦法將其拿去快洗。

所以我們每次將第 \(i-m\) 天前的舊餐巾全部累加至第 \(i-m\) 天即可。這樣可以優化一定的常數。

這樣可以保證在計算慢洗的時候一定是線性的,但仍然不能保證計算快洗部分時為線性。

最后我們需要確定最優解時需購買餐巾的張數 \(c\)

設最優解時最小代價為 \(f(c)\),設當前枚舉到了 \(x\)

則當 $x \ge c $,一定有 \(f(x) \ge f(c)\),因為你還需要多出錢買新餐巾。

\(x \le c\),則要么不存在 \(f(x)\) ,即購買 \(x\) 張餐巾不能達到目的,要么此時多洗一張餐巾一定劣於多買一張餐巾,也有 \(f(x) \ge f(c)\)

所以對於最優解 \(c\) 我們可以三分求解。

個人認為代碼可讀性還是挺高的,可以康康代碼:

/*---Author:HenryHuang---*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=5e5+5;
const ll inf=1ll<<50;
ll sumr=0;
ll r[maxn],res[maxn];
ll n,m1,m2,c1,c2,p;
ll f(ll num){
	ll ans=1ll*num*p;//提前算好新餐巾代價 
	ll now=0;
	for(ll i=1;i<=n;++i){
		res[i]=0;
		if(r[i]){
			ll tmp=r[i];
			if(num){
				ll k=min(tmp,num);
				tmp-=k,res[i]+=k,num-=k;
				if(!tmp) continue;
			}//直接用新的 
			for(ll j=now;j<i-m2;++j){
				res[j+1]+=res[j],res[j]=0;
			}//累加舊洗餐巾 
			now=max(now,i-m2);
			if(now<i&&res[now]){
				ll k=min(tmp,res[now]);
				res[now]-=k,res[i]+=k;
				tmp-=k,ans+=1ll*k*c2;
				if(!tmp) continue;
			}//慢洗 
			for(ll j=i-m1;j>=1&&j>i-m2;--j){
				if(res[j]){
					ll k=min(tmp,res[j]);
					res[j]-=k,res[i]+=k;
					tmp-=k,ans+=1ll*k*c1;
				}
				if(!tmp) break;
			}//快洗 
			if(tmp) return inf;
		}
	}
	return ans;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m1>>m2>>c1>>c2>>p;
	if(m1>m2) swap(m1,m2),swap(c1,c2);//1快2慢
	if(c1<c2) c2=c1,m2=m1; 
	for(ll i=1;i<=n;++i)
		cin>>r[i],sumr+=r[i];
	ll l=1,r=max(10000ll,sumr/3);//奇淫技巧 
	ll ans=inf;
	while(r-l>2){
		ll k=(r-l)/3;
		ll mid1=l+k,mid2=r-k;
		ll aa=f(mid1),bb=f(mid2);
		if(aa<bb) ans=min(ans,aa),r=mid2;
		else ans=min(ans,bb),l=mid1;
	}
	for(ll i=l;i<=r;++i) ans=min(ans,f(i));
	cout<<ans<<'\n';
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM