【计蒜客 - 蓝桥训练】轻重搭配(贪心,STLset 或 二分)
題干:
n?個(gè)同學(xué)去動(dòng)物園參觀,原本每人都需要買(mǎi)一張門(mén)票,但售票處推出了一個(gè)優(yōu)惠活動(dòng),一個(gè)體重為?xx?的人可以和體重至少為?2x2x?配對(duì),這樣兩人只需買(mǎi)一張票。現(xiàn)在給出了?nn?個(gè)人的體重,請(qǐng)你計(jì)算他們最少需要買(mǎi)幾張門(mén)票?
輸入格式
第一行一個(gè)整數(shù)?nn,表示人數(shù)。
第二行?nn?個(gè)整數(shù),每個(gè)整數(shù)?a_iai??表示每個(gè)人的體重。
輸出格式
一個(gè)整數(shù),表示最少需要購(gòu)買(mǎi)的門(mén)票數(shù)目。
數(shù)據(jù)范圍
對(duì)于?30\%30%?的數(shù)據(jù):1 \le n \le 251≤n≤25,1\le a_i \le 1001≤ai?≤100。
對(duì)于?60\%60%?的數(shù)據(jù):1 \le n \le 100001≤n≤10000,1\le a_i \le 10001≤ai?≤1000。
對(duì)于?100\%100%?的數(shù)據(jù):1 \le n \le 5\cdot 10^51≤n≤5?105,1\le a_i \le 10^51≤ai?≤105。
樣例解釋
11?和?99?配對(duì),77?和?33?配對(duì),剩下?5,55,5?單獨(dú),一共買(mǎi)四張票。
樣例輸入復(fù)制
6 1 9 7 3 5 5樣例輸出復(fù)制
4解題報(bào)告:
? ? 這題貪心證明(同優(yōu)則立法),一定可以從前n/2個(gè)數(shù)中選擇就行了,然后從后面剩下的數(shù)(可能n/2 也可以 n/2 +1)中選擇配對(duì)的。。
錯(cuò)誤代碼:
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define ll long long #define pb push_back #define pm make_pair #define fi first #define se second using namespace std; const int MAX = 2e5 + 5; set<int> ss; set<int> :: iterator it,itt; int main() {int n;cin>>n;for(int tmp,i = 1; i<=n; i++) {scanf("%d",&tmp);ss.insert(tmp);}int ans = 0;while(ss.size()) {it = ss.begin();itt = ss.lower_bound(*it * 2);if(itt == ss.end()) break;else ss.erase(itt),ss.erase(it),ans++;}cout << ss.size() + ans << endl;return 0 ;} /* 6 1 3 5 5 7 9 */一改:
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define ll long long #define pb push_back #define pm make_pair #define fi first #define se second using namespace std; const int MAX = 2e5 + 5; int a[MAX]; set<int> ss; set<int> :: iterator it; int main() {int n;cin>>n;for(int i = 1; i<=n; i++) {scanf("%d",a+i);}sort(a+1,a+n+1);for(int i = n/2+1; i<=n; i++) {ss.insert(a[i]);}int cur = 1;while(ss.size() && cur <= n/2) {it = ss.lower_bound(a[cur] * 2); // printf("it = %d\n",*it);if(it == ss.end()) break;else ss.erase(it);cur++;}cur--;cout << /*ss.size() + n/2 - cur*/n-cur << endl;return 0 ;} /* 6 1 3 5 5 7 9 */AC代碼:
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define ll long long #define pb push_back #define pm make_pair #define fi first #define se second using namespace std; const int MAX = 6e5 + 5; int a[MAX]; multiset<int> ss; multiset<int> :: iterator it; int main() {int n;cin>>n;for(int i = 1; i<=n; i++) {scanf("%d",a+i);}sort(a+1,a+n+1);for(int i = n/2+1; i<=n; i++) {ss.insert(a[i]);}int ans = 0;int cur = 1;while(ss.size() && cur <= n/2) {it = ss.lower_bound(a[cur] * 2); // printf("it = %d\n",*it);if(it == ss.end()) break;else ss.erase(it),ans++;cur++;}cur--;cout << ans + (n/2 - cur) + ss.size() << endl;return 0 ;} /* 6 1 3 5 5 7 9 */注意點(diǎn):最后答案的計(jì)算,來(lái)自三部分別忘了(其實(shí)根據(jù)標(biāo)程那個(gè)想法更好想)。
? cur從1開(kāi)始而非從0開(kāi)始。
? ?用multiset,而不是set。
? 還有一個(gè)坑點(diǎn),,數(shù)據(jù)范圍是5e5,,我一般習(xí)慣2e5了,,所以一直WA。。
或者最后答案這么計(jì)算:
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define ll long long #define pb push_back #define pm make_pair #define fi first #define se second using namespace std; const int MAX = 6e5 + 5; int a[MAX]; multiset<int> ss; multiset<int> :: iterator it; int main() {int n;cin>>n;for(int i = 1; i<=n; i++) {scanf("%d",a+i);}sort(a+1,a+n+1);for(int i = n/2+1; i<=n; i++) {ss.insert(a[i]);}int cur = 1;while(ss.size() && cur <= n/2) {it = ss.lower_bound(a[cur] * 2); // printf("it = %d\n",*it);if(it == ss.end()) break;else ss.erase(it);cur++;}cur--;cout << /*ss.size() + n/2 - cur*/n-cur << endl;return 0 ; } /* 6 1 3 5 5 7 9 */標(biāo)準(zhǔn)的SET的AC代碼(233ms):
#include <algorithm> #include <iostream> #include <set> using namespace std; int a[1000005]; int main() {int n;scanf("%d", &n);for (int i = 0; i < n; i++) {scanf("%d", &a[i]);}sort(a, a + n);multiset<int> se;for (int i = n / 2; i < n; i++) {se.insert(a[i]);}int ans = n;for (int i = 0; i < n / 2; i++) {multiset<int>::iterator it = se.lower_bound(a[i] * 2);if (it == se.end()) break;se.erase(it);ans--;}printf("%d\n", ans);return 0; }另一個(gè)線性的做法(不用SET的標(biāo)程82ms):
#include <algorithm> #include <cstdio> using namespace std; int a[500005]; int main() {int n;scanf("%d", &n);for (int i = 0; i < n; i++) {scanf("%d", &a[i]);}sort(a, a + n);int ans = n, pos = n / 2;for (int i = 0; i < n / 2; i++) {while (pos < n && a[pos] < a[i] * 2) pos++;if (pos == n) break;ans--;pos++;}printf("%d\n", ans);return 0; }這題還可以二分:
后來(lái)就想到,如果某種方案配對(duì)了x對(duì),那么我們可以統(tǒng)一將這x對(duì)中比較大的數(shù)字,從大到小依次用a[n],a[n-1]……a[n-x+1]給替換掉;而比較小的數(shù)字從小到大依次用a[1]……a[x]替換掉,可知替換出來(lái)還是滿足條件的方案。另外我們將每一對(duì)視作一條邊,連接小數(shù)字的有序數(shù)列和大數(shù)字的有序數(shù)列,那么會(huì)產(chǎn)生一個(gè)二分圖。
?如果兩條邊出現(xiàn)交叉,也就是出現(xiàn)a[i]<a[j], a[ii]>a[jj],a[i]*2<=a[ii],a[j]*2<=a[jj]。那么可以將i連接jj而j連接ii。經(jīng)過(guò)一陣重新連接,我們發(fā)現(xiàn),可能的方案等價(jià)于a[1]連接a[n-x+1]……a[x]連接a[n],于是現(xiàn)在我們只需要找出具體對(duì)于哪個(gè)x成立就行了,所以對(duì)于這種,已知答案就可以推算是否合理的題目,我們選擇二分。
?
總結(jié)
以上是生活随笔為你收集整理的【计蒜客 - 蓝桥训练】轻重搭配(贪心,STLset 或 二分)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Winpack.exe - Winpac
- 下一篇: 【HDU - 3870】Catch th