The 10th Shandong Provincial Collegiate Programming Contest H.Tokens on the Segments(贪心+优先级队列 or 贪心+暴力)
•题意
二维平面上有 n 条线段,每条线段坐标为 $(l_i,i),(r_i,i)$;
平面上的每个整点坐标上都可以放置一枚硬币,但是要求任意两枚硬币的横坐标不相同;
问最多有多少条线段可以放置硬币。
•题解1
考虑到当 $X=x$ 时,最多有一条线段可以放置一枚硬币;
那么,我们可以从左到右查找最多有多少条线段可以放置硬币;
定义变量 $X$ 表示 $[0,X]$ 位置已放置好硬币;
既然是按照 $x$ 来的,那么我们就需要将所有可能可以放置硬币的线段按照 $l$ 升序排列,如果 $l$ 相同,按照 $r$ 升序排列;
考虑用优先级队列,首先将所有线段放入优先级队列 $q$ 中,并定义 $X=0$;
每次选择从 $q$ 的队头取出 $l$ 小的线段,判断这条线段的 $l'$ 与 $X$ 的位置关系:
①如果 $l' \leq X$,说明当前这条线段的 $[l',X]$ 位置 不能放置硬币,只能考虑 $[X+1,r']$ 位置是否还可以放置硬币;
那么,此时,我们就将 $[X+1,r_i]$ 丢到 $q$ 中,代表可能从 $[X+1,r']$ 中选择某位置放置硬币;
②如果 $l' > X$,说明 $[X,l')$ 间无可放置硬币的线段,那么我们要选择一枚硬币放置在 $l'$ 处,即当前这条线段上,并更新 $X=l'$;
•Code
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e5+50; 4 5 int n; 6 struct Heap 7 { 8 int l,r; 9 bool operator < (const Heap &obj)const 10 { 11 if(l != obj.l) 12 return l > obj.l; 13 return r > obj.r; 14 } 15 }; 16 priority_queue<Heap >q; 17 18 int Solve() 19 { 20 int X=0; 21 int ans=0; 22 while(!q.empty()) 23 { 24 Heap tmp=q.top(); 25 q.pop(); 26 27 /** 28 如果 tmp.l <= X,那么[tmp.l,X]是已求出最解的位置 29 但是[X+1,tmp.r] 还是没有放置硬币的 30 所以当前线段还是有可能在[X+1,tmp.rr]区间放置一枚硬币的 31 所以将其加入到q中 32 */ 33 if(tmp.l <= X && X+1 <= tmp.r) 34 q.push({X+1,tmp.r}); 35 else if(tmp.l > X)///如果tmp.l > X,更新ans,X 36 { 37 ans++; 38 X=tmp.l; 39 } 40 } 41 return ans; 42 } 43 int main() 44 { 45 int T; 46 scanf("%d",&T); 47 while(T--) 48 { 49 while(!q.empty()) 50 q.pop(); 51 52 scanf("%d",&n); 53 for(int i=1;i <= n;++i) 54 { 55 int l,r; 56 scanf("%d%d",&l,&r); 57 q.push({l,r}); 58 } 59 printf("%d\n",Solve()); 60 } 61 return 0; 62 }
•题解2
贪心+暴力
贪心策略:按 $r$ 从小到大排,$r$ 相同按 $l$ 从小到大排;
从 1~n 遍历每个线段,对于第 i 条线段,暴力查找 $[l,r]$ 最左的为放置硬币的空位置;
•Code
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define ls(x) (x<<1) 5 #define rs(x) (x<<1|1) 6 const int maxn=1e5+50; 7 8 int n; 9 set<int >_set; 10 struct Date 11 { 12 int l,r; 13 int len; 14 bool operator < (const Date &obj) const 15 { 16 return r < obj.r; 17 } 18 }_date[maxn]; 19 20 int Solve() 21 { 22 sort(_date+1,_date+n+1); 23 _set.clear(); 24 25 int ans=0; 26 for(int i=1;i <= n;++i) 27 { 28 int l=_date[i].l; 29 int r=_date[i].r; 30 for(int j=l;j <= r;++j) 31 { 32 if(_set.find(j) == _set.end())///查找第i条线段可以放置硬币的最左的位置 33 { 34 _set.insert(j); 35 ans++; 36 break; 37 } 38 } 39 } 40 return ans; 41 } 42 int main() 43 { 44 int test; 45 scanf("%d",&test); 46 while(test--) 47 { 48 scanf("%d",&n); 49 for(int i=1;i <= n;++i) 50 { 51 scanf("%d%d",&_date[i].l,&_date[i].r); 52 _date[i].len=_date[i].r-_date[i].l+1; 53 } 54 printf("%d\n",Solve()); 55 } 56 return 0; 57 }
•题解2分析
如果输入 1e5 个线段,所有线段的左右端点全部为 [1,1e9];
那么,这个算法的时间复杂度为 O(n2logn);
这个时间复杂度在打比赛的时候是不敢想的啊;
虽然不能说是正解,但可以借鉴一下其贪心的思路(tql);
•疑惑
这道题在离散化后跑一边方法①的代码wa了???
感觉,离散化后不影响结果啊??