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

 1 #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
",Solve());
60     }
61     return 0;
62 }
View Code

  

•题解2

  贪心+暴力

  贪心策略:按 $r$ 从小到大排,$r$ 相同按 $l$ 从小到大排;

  从 1~n 遍历每个线段,对于第 i 条线段,暴力查找 $[l,r]$ 最左的为放置硬币的空位置;

•Code

 1 #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
",Solve());
55     }
56     return 0;
57 }
View Code

•题解2分析

  如果输入 1e5 个线段,所有线段的左右端点全部为 [1,1e9];

  那么,这个算法的时间复杂度为 O(n2logn);

  这个时间复杂度在打比赛的时候是不敢想的啊;

  虽然不能说是正解,但可以借鉴一下其贪心的思路(tql);

•疑惑

  这道题在离散化后跑一边方法①的代码wa了???

  感觉,离散化后不影响结果啊??

原文地址:https://www.cnblogs.com/violet-acmer/p/10872557.html