2 条题解

  • 0
    @ 2026-9-28 10:50:29

    看到题解区没有贪心解法,来交一发。蒟蒻第一次做证明,如有勘误,敬请指教。

    该题的本质是从 nn 个区间里选择一些区间,在这些区间能覆盖 [1,t][1,t] 内所有整数点的前提下,使得选择的区间个数最小。

    算法流程:

    1. 将所有区间按左端点从小到大排序。

    2. 设 stst 为当前最靠左的没有被覆盖的点。枚举每个区间,在所有能覆盖 stst 的区间中,挑一个右端点最大的作为当前决策。然后更新 stst。

    3. 如果找不到能覆盖 stst 的区间,则原问题无解。

    利用双指针实现算法。

    #include <bits/stdc++.h>
    using namespace std;
    
    struct segment{
    	int l,r;
    	bool operator<(const segment &x) const{
    		return l<x.l;
    	} // 重载小于号,用于 sort()。也可以用 cmp() 函数实现同样功能。
    }range[25005];
    
    int main(){
    	int n,ed;
    	scanf("%d%d",&n,&ed);
    	for(int i=1;i<=n;++i)
    		scanf("%d%d",&range[i].l,&range[i].r);
    	sort(range+1,range+n+1);
    	int st=1,ans=0;
    	for(int i=1,j=1;i<=n;){
    		int r=0;
    		while(j<=n&&range[j].l<=st){ // 左端点要 <= st,即该区间能覆盖 st 
    			r=max(r,range[j].r); // 找右端点最大的
    			j++;
    		}
    		if(r<st) break; // 找不到能覆盖 st 的区间
    		ans++;
    		if(r>=ed){ // 有解,输出
    			printf("%d\n",ans);
    			return 0;
    		}
    		st=r+1;i=j;
    	}
    	printf("-1\n");
    	return 0;
    }
    

    证明:

    参考闫学灿《算法基础课》。

    只考虑有解的情况。

    • 正确性:由于我们每次选择的左端点 ≤\le 上次选择的右端点,所以能保证任意两个区间都是连接的。所以每个整数点都一定会被覆盖到。

    • 最优性:假设最优解与该算法得出的解有部分区间选择不同。由于该算法中每次选择右端点最大的区间,所以一定能够覆盖更多的点。如果我们把最优解中选择的区间换成该算法选择的区间,显然答案不会更劣。

    综上所述,以上贪心算法能得出最优解。

    • 0
      @ 2025-10-8 17:00:13
      //Author:XuHt
      #include <cstdio>
      #include <cstring>
      #include <iostream>
      #include <algorithm>
      using namespace std;
      const int N = 100006, INF = 0x3f3f3f3f;
      int n, m, f[N], b[N], tot = 0;
      struct T {
          int l, r, x;
          bool operator < (const T w) const {
              return r < w.r;
          }
      } a[N], t[N<<2];
      
      void build(int p, int l, int r) {
          t[p].l = l;
          t[p].r = r;
          t[p].x = l ? INF : 0;
          if (l == r) return;
          int mid = (l + r) >> 1;
          build(p << 1, l, mid);
          build(p << 1 | 1, mid + 1, r);
      }
      
      void change(int p, int x, int y) {
          if (t[p].l == t[p].r) {
              t[p].x = y;
              return;
          }
          int mid = (t[p].l + t[p].r) >> 1;
          if (x <= mid) change(p << 1, x, y);
          else change(p << 1 | 1, x, y);
          t[p].x = min(t[p<<1].x, t[p<<1|1].x);
      }
      
      int ask(int p, int l, int r) {
          if (t[p].l >= l && t[p].r <= r) return t[p].x;
          int mid = (t[p].l + t[p].r) >> 1, ans = INF;
          if (l <= mid) ans = min(ans, ask(p << 1, l, r));
          if (r > mid) ans = min(ans, ask(p << 1 | 1, l, r));
          return ans;
      }
      
      int main() {
          cin >> n >> m;
          b[++tot] = 1;
          for (int i = 1; i <= n; i++) {
              scanf("%d %d", &a[i].l, &a[i].r);
              b[++tot] = a[i].l;
              b[++tot] = a[i].l + 1;
              b[++tot] = a[i].r;
              b[++tot] = a[i].r + 1;
          }
          b[++tot] = m;
          sort(b + 1, b + tot + 1);
          tot = unique(b + 1, b + tot + 1) - (b + 1);
          while (b[tot] > m) --tot;
          sort(a + 1, a + n + 1);
          build(1, 0, tot);
          memset(f, 0x3f, sizeof(f));
          f[0] = 0;
          for (int i = 1; i <= n; i++) {
              a[i].r = lower_bound(b + 1, b + tot + 1, a[i].r) - b;
              a[i].l = lower_bound(b + 1, b + tot + 1, a[i].l) - b;
              int num = ask(1, a[i].l - 1, a[i].r - 1) + 1;
              if (f[a[i].r] > num) {
                  f[a[i].r] = num;
                  change(1, a[i].r, f[a[i].r]);
              }
          }
          if (f[tot] == INF) puts("-1");
          else cout << f[tot] << endl;
          return 0;
      }
      
      • 1

      信息

      ID
      2180
      时间
      1000ms
      内存
      512MiB
      难度
      9
      标签
      递交数
      11
      已通过
      6
      上传者