【计蒜客 - 蓝桥训练】轻重搭配(贪心,STLset 或 二分)
題干:
n?個同學去動物園參觀,原本每人都需要買一張門票,但售票處推出了一個優惠活動,一個體重為?xx?的人可以和體重至少為?2x2x?配對,這樣兩人只需買一張票?,F在給出了?nn?個人的體重,請你計算他們最少需要買幾張門票?
輸入格式
第一行一個整數?nn,表示人數。
第二行?nn?個整數,每個整數?a_iai??表示每個人的體重。
輸出格式
一個整數,表示最少需要購買的門票數目。
數據范圍
對于?30\%30%?的數據:1 \le n \le 251≤n≤25,1\le a_i \le 1001≤ai?≤100。
對于?60\%60%?的數據:1 \le n \le 100001≤n≤10000,1\le a_i \le 10001≤ai?≤1000。
對于?100\%100%?的數據:1 \le n \le 5\cdot 10^51≤n≤5?105,1\le a_i \le 10^51≤ai?≤105。
樣例解釋
11?和?99?配對,77?和?33?配對,剩下?5,55,5?單獨,一共買四張票。
樣例輸入復制
6 1 9 7 3 5 5樣例輸出復制
4解題報告:
? ? 這題貪心證明(同優則立法),一定可以從前n/2個數中選擇就行了,然后從后面剩下的數(可能n/2 也可以 n/2 +1)中選擇配對的。。
錯誤代碼:
#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 */注意點:最后答案的計算,來自三部分別忘了(其實根據標程那個想法更好想)。
? cur從1開始而非從0開始。
? ?用multiset,而不是set。
? 還有一個坑點,,數據范圍是5e5,,我一般習慣2e5了,,所以一直WA。。
或者最后答案這么計算:
#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 */標準的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; }另一個線性的做法(不用SET的標程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; }這題還可以二分:
后來就想到,如果某種方案配對了x對,那么我們可以統一將這x對中比較大的數字,從大到小依次用a[n],a[n-1]……a[n-x+1]給替換掉;而比較小的數字從小到大依次用a[1]……a[x]替換掉,可知替換出來還是滿足條件的方案。另外我們將每一對視作一條邊,連接小數字的有序數列和大數字的有序數列,那么會產生一個二分圖。
?如果兩條邊出現交叉,也就是出現a[i]<a[j], a[ii]>a[jj],a[i]*2<=a[ii],a[j]*2<=a[jj]。那么可以將i連接jj而j連接ii。經過一陣重新連接,我們發現,可能的方案等價于a[1]連接a[n-x+1]……a[x]連接a[n],于是現在我們只需要找出具體對于哪個x成立就行了,所以對于這種,已知答案就可以推算是否合理的題目,我們選擇二分。
?
總結
以上是生活随笔為你收集整理的【计蒜客 - 蓝桥训练】轻重搭配(贪心,STLset 或 二分)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Winpack.exe - Winpac
- 下一篇: winproj.exe - winpro