题意:
给出一个数组,删除大于等于k的数字,使得其满足以下条件:
1.剩余的连续的段,每一段的长度相等;
2.在满足第一个条件的情况下,段数尽可能多;
3.在满足前两个条件的情况下,k取最小的。
求k。
思路:
一开始整个数组可以看成完整的一段,这是对应的k是最大的数字 + 1。
用一个set sd维护删除的数字。
从大到小枚举删除的数字,每次删除一个数字,都可以看成是删除一个长的段,然后添加两个小的段,在sd中找出这个数字的位置cur的前驱pre和后继sub,要删除的段就是前驱到后继这一段,然后新添加两段就是cur到pre 以及 sub到cur。把cur添加到删除数字的集合中。
用一个map维护<线段长度,数量>来表示现在段数的情况,容易知道当map的size为1时,表明所有线段的长度相同,就可以更新答案了。
更新答案的时候,首先考虑线段数量是否更多,再考虑k是否可以变小。
假设当前删除的数字是tmp,那么是不是满足条件之后就直接把答案更新为tmp呢?
不是,假设当前数字是x,比它小的第一个数字是y,显然y + 1这个数字也可以满足条件,而且y + 1 <= x是显然的,所以答案应该更新为y + 1。
感谢mzjj教我!
代码:
1 #include <stdio.h> 2 #include <string.h> 3 #include <algorithm> 4 #include <map> 5 #include <set> 6 using namespace std; 7 typedef long long ll; 8 typedef pair<int,int> pii; 9 map<int,int> mmp; 10 set<pii> s; 11 set<int> sd; 12 int main() 13 { 14 int n; 15 scanf("%d",&n); 16 for (int i = 1;i <= n;i++) 17 { 18 int x; 19 scanf("%d",&x); 20 s.insert(pii(x,i)); 21 } 22 sd.insert(0); 23 sd.insert(n+1); 24 mmp[n]++; 25 int ans = s.rbegin() -> first + 1; 26 int cnt = 1; 27 for (int i = 0;i < n - 1;i++) 28 { 29 auto it = s.rbegin(); 30 int cur = it -> second; 31 s.erase(*it); 32 int pre = *(--sd.lower_bound(cur)); 33 int sub = *(sd.upper_bound(cur)); 34 //printf("%d %d %d ",pre,sub,cur); 35 if (sub-pre-1 > 0) 36 { 37 mmp[sub-pre-1]--; 38 if (mmp[sub-pre-1] == 0) 39 { 40 mmp.erase(sub-pre-1); 41 } 42 } 43 sd.insert(cur); 44 if (cur-pre-1 > 0) mmp[cur-pre-1]++; 45 if (sub-cur-1 > 0) mmp[sub-cur-1]++; 46 if (mmp.size() == 1) 47 { 48 //int len = mmp.begin() -> first; 49 int num = mmp.begin() -> second; 50 //printf("%d * ",len); 51 if (num >= cnt) 52 { 53 cnt = num; 54 ans = s.rbegin() -> first + 1; 55 } 56 } 57 } 58 printf("%d",ans); 59 return 0; 60 }