1. 程式人生 > 實用技巧 >Reading Books (easy version) -- 貪心,字首和/STL

Reading Books (easy version) -- 貪心,字首和/STL

Reading Books (easy version)

原題連結:傳送門

題目大意

你有n本書,每本書有三個附帶資訊 t , a , b.

t: 讀完這本書所花費的時間

a: 表示Alice是否喜歡這本書

b: 表示Bob是否喜歡這本書

0 表示不喜歡 1 表示喜歡。現在給你這 n 本書的資訊,要求Alice和Bob沒人必須讀k本書,求讀完這本書的最小時間代價。如果不存在答案則輸出-1

分析

這個題意得話,一眼望過去就知道是貪心 + 排序,於是我一開始對所有得書本進行排序讓公共喜歡得書籍放在前面,其餘得按照時間排序,於是WA on test 5.


於是開始轉換思路,因為上面得方法存在漏洞,就是如果我當前選擇依次讀完兩本書得話還不如兩個人各自讀完一本書所消耗得代價小的話那麼我還不如讓其分開去讀。

這樣確立貪心策略:兩個人同時選擇自己喜歡的書,且選擇當前消耗代價總和最小的那一種情況。

這題的思路其實很好想,關鍵在於你要如何實現這個方法。這也是對這道題寫題解的原因。提升一下自己的程式碼能力,對於邊界情況和情況分類要卡好否則就會WA掉

AC 程式碼

自己AC程式碼

void slove()
{
	int n, k; cin >> n >> k;
	vector<int> com, Ali, Bob;
	for (int i = 0; i < n; i++)
	{
		int t, a, b;
		cin >> t >> a >> b;
		if ((a + b) == 2)com.PB(t);
		if (a == 1 && b == 0)Ali.PB(t);
		if (b == 1 && a == 0)Bob.PB(t);
	}
	sort(com.begin(), com.end(),greater<int>());
	sort(Ali.begin(), Ali.end(),greater<int>());
	sort(Bob.begin(), Bob.end(),greater<int>());
	int ic = com.size() - 1 , ia = Ali.size() - 1 , ib = Bob.size() - 1;
	int ans = 0,A = k , B = k;
	while(ic >= 0 || (ia >= 0 && ib >= 0))
	{
		if(A == 0 && B == 0)break;
		if((ia < 0 || ib < 0) && ic >= 0) // 如果兩個之中有一個沒有了
		{
			ans += com[ic];ic--;A--,B--;
			continue;
		}
		if((ic >= 0) && com[ic] <= Bob[ib] + Ali[ia])
		{
			ans += com[ic];ic--;A--,B--;
		}else {
			if(ib >= 0)ans += Bob[ib],ib--,B--;
			if(ia >= 0)ans += Ali[ia],ia--,A--;
		}
	}
	if(A > 0 || B > 0)cout << -1 << endl;
	else cout << ans << endl;
}

標程/優秀程式碼

題解程式碼中巧妙地運用了字首和地思想極大地簡化了程式碼。

void slove()
{
		int n , k;
	cin >> n >> k;
	vector<int> times[4];
	vector<int> sums[4];
	for (int i = 0; i < n; ++i) 
	{
		int t, a, b;
		cin >> t >> a >> b;
		times[a * 2 + b].push_back(t);
	}
	// 對每一個序列進行排序並獲取其字首和
	for (int i = 0; i < 4; ++i) 
	{
		sort(times[i].begin(), times[i].end());
		sums[i].push_back(0);
		for (auto it : times[i]) 
		{
			sums[i].push_back(sums[i].back() + it);
		}
	}
	int ans = MAX;
	// 列舉去了多少次公共的部分 如果不存在
	for(int i = 0;i < min(k + 1,(int)sums[3].size());i ++)
	{
		// 取完公共的部分取剩下的部分即 k - i 但是要確保k - i在每個序列範圍內才進行更新 ans
		if(k - i  < sums[1].size() && k - i < sums[2].size())
		{
			ans = min(ans ,sums[3][i] + sums[1][k-i] + sums[2][k-i]);
		}
	}
	if(ans == MAX)ans = -1;
	cout << ans << endl;
}