貪心

和蜀玩發表於2024-08-06

有些題 dp 是難做的或做不了的,這個時候可以去設計一種策略使得決策儘可能最優,也就是貪心。可以說貪心有時候是一種亂搞,但亂搞也能搞出出題人想不到的正解。

反悔貪心

有些題中直接 dp 的複雜度很高並且很難最佳化或者有後效性無法 dp,樸素貪心考慮可以做到 \(O(n)\) 但是無法保證正確性,這個時候可以使用反悔貪心,在一般是 \(O(n\log n)\) 的時間內完成問題的求解。

相對於普通貪心而言,反悔貪心特殊的點在於“反悔”操作。普通貪心在完成某個階段的求解後就不會再管那個階段的決策是否最優,求的是區域性最優解而不一定是全域性最優解,但反悔貪心可以改變之前的決策,即“反悔”操作。按照判斷方式的不同,反悔貪心可以分為反悔堆反悔自動機兩種方法,一般都用到了優先佇列實現。

大概流程:先按普通貪心的思路做,用一個優先佇列記錄每個決策點的決策,當判斷出不優的情況就從優先佇列中取出最大/小的元素,也就是對之前的決策進行反悔操作,並更新答案。反悔貪心有一些模型,並且貪心題都還是重在思維的鍛鍊和積累,多做點題吧,還能學 OI 的日子不多了。

P2107 小Z的AK計劃

價值一定模型。但這個題比較簡單。

首先很明顯,我們一定是按 \(x_i\) 從小到大來選點,回頭一定不優,所以先把所有點按 \(x\) 排序。貪心過程中每次加入點時先累加上所需時間,把這個點塞入大根堆中,如果累計花費時間 \(>m\) 那麼就將堆頂元素,也就是耗時最大的點取出,總時間減去 \(t_i\),但注意不要減去 \(x_i-x_{i-1}\),因為路過的時間是必須要算的。還有注意每次取完點後答案都要取最大值。

點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pi pair<ll,ll>
#define fi first
#define se second
const ll N=114514,M=1919810,inf=1e18;
ll n,m,ans,res;
priority_queue <ll> q;
struct xx{
	ll x,t;
}a[N];
bool cmp(xx x,xx y){
	return x.x<y.x;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;++i) cin>>a[i].x>>a[i].t;
	sort(a+1,a+n+1,cmp);
	ll sum=0;
	for(int i=1;i<=n;++i){
		if(a[i].x>m) break;
		sum+=a[i].t+a[i].x-a[i-1].x,++res;
		q.push(a[i].t);
		if(sum>m){
			sum-=q.top(),--res;
			q.pop();
		}
		ans=max(ans,res);
	}
	cout<<ans;
	return 0;
}//好像也沒多難? 

相關文章