1. 程式人生 > 其它 >CF1303D Fill The Bag 題解

CF1303D Fill The Bag 題解

可能更好的閱讀體驗
題目傳送門
我來氵題解了

題目大意

給定一個大小為 \(k\) 的揹包以及 \(n\) 個物體。第 \(i\) 個物體的體積為 \(a_i\),並且保證 \(a_i\)\(2\) 的非負整數冪。現在你可以將一些物品切開,變成兩個只有體積為原來一半的物體,求能把揹包恰好裝滿至少需要切幾次。

題目解析

不難想到二進位制拆分。考慮貪心。
二進位制拆分後,如果這一位沒有大小恰好為 \(2^i\) 的物體,那麼就去更大的切分。
如果從高到低考慮,我們不難發現這是假的,所以考慮從低到高位考慮。

具體的做法是這樣的:
從低位到高位考慮,如果這一位恰好有大小為 \(2^i\) 的物體,那麼就不需要切割,否則就找到最小的更大的物體來切割。

現在來證明這種做法的正確性和時間複雜度。
先證明正確性。不難發現如果將一個大小為 \(2^c\) 的物體分割成大小為 \(2^a\) 的物體,和把一個大小為 \(2^c\) 的物體分割成 \(2^b\) 的物體再分割成 \(2^a\) 的物體所需的次數是一樣的(\(a<b<c\))。所以這個做法是正確的。
然後證明時間複雜度。如果我們將分割了一個大小為 \(2^b\) 的物體到 \(2^a\) 的大小,那麼就會多出 \(2^{a+1},2^{a+2},\dots,2^{b-2},2^{b-1}\) 這些物體。因此在暴力向上查詢最小的更大的物體切割的時候,每個物體最多被搜到一次,所以可以保證時間複雜度為 \(\Theta\left(n\right)\)

無解的情況是顯然的,因為切割的時候總體積不變,所以只要 \(\sum a_i<k\) 的時候就無解,否則是有解的,因為你可以把所以的物體切成大小為 \(1\) 的大小。

核心程式碼:

int n,x,ans,t[39]; ll m,sum;
void work(){
	m=read(); n=read(); Me(t,0); sum=0; ans=0; int i,j;
	for(i=1;i<=n;i++) x=read(),sum+=x,t[(int)log2(x)]++;
	if(sum<m){ puts("-1"); return; } if(sum==m){ puts("0"); return; }
	for(i=0;i<31;i++){
		if(!t[i]&&(m&(1<<i))){ j=i; while(!t[j]) j++,ans++; t[j]--; for(j=j-1;j>=i;j--) t[j]++; }
		if(m&(1<<i)) t[i]--; t[i+1]+=(t[i]>>1);
	} print(ans),pc('\n'); return; 
}