1. 程式人生 > >3個典型區間貪心問題(總結篇)

3個典型區間貪心問題(總結篇)

  1.數軸上有n個閉區間[ai,bi]。取儘量少的點,使得每個區間內都至少有一個點(不同區間內含的點可以是同一個)。

  2.數軸上有n個開區間(ai,bi)。最多能有多少個互不相交的區間?

  3.數軸上有n個區間[ai,bi],選擇儘量少的區間覆蓋一條指定線段[s,t]。

  解法:

        1.

          貪心策略:

          按照b1<=b2<=b3…(b相同時按a從大到小)的方式排序排序,從前向後遍歷,當遇到沒有加入集合的區間時,選取這個區間的右端點b。

          程式碼:

#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
typedef struct node
{
         int l,r;
}node;
node p[100005];
bool cmp(node a,node b){
         return a.r<b.r;
}
int main()
{
         int T;
         scanf("%d",&T);
         while(T--){
                  int n;
                  scanf("%d",&n);
                  for(int i=0;i<n;i++)
                           scanf("%d%d",&p[i].l,&p[i].r);
                  sort(p,p+n,cmp);
                  int now=-1;
                  int ans=0;
                  for(int i=0;i<n;i++){
                           if(p[i].l>now){
                                    now=p[i].r;
                                    ans++;
                           }
                  }
                  printf("%d\n",ans);
         }
         return 0;
}
       2. 

       貪心策略:

       按照b1<=b2<=b3…的方式排序,然後從前向後遍歷,每當遇到可以加入集合的區間,就把它加入集合。(集合代表解的集合)

       程式碼:

    #include <stdio.h>  
    #include <algorithm>  
    using namespace std;  
    struct Extent  
    {  
        int a,b;  
        bool operator < (const Extent& S)const  
        {  
            return b < S.b;  
        }  
    }A[10002];  
    int main()  
    {  
        int z,n,cnt,end;  
        scanf("%d",&z);  
        while(z--)  
        {  
            cnt = 0;  
            end = -1;  
            scanf("%d",&n);  
            for(int i=0;i<n;i++)  
                scanf("%d%d",&A[i].a,&A[i].b);  
            sort(A,A+n);  
            for(int i=0;i<n;i++)  
            {  
                if(end < A[i].a)  
                {  
                    end = A[i].b;  
                    cnt++;  
                }  
            }  
            printf("%d\n",cnt);  
        }  
        return 0;  
    }  
    3.

     貪心策略:

     把各區間按照a從小到大排序,從前向後遍歷,然後每次選擇從當前起點S開始的最長區間,並以這個區間的右端點為新的起點,繼續選擇,直到找不到區間覆蓋當前起點S或者S已經到達線段末端。

需要注意的是,如果某一區間邊界大於s,t的邊界,應把它們變成s或t。因為超出的部分毫無意義,同時還會影響對資料的分析。

程式碼:

    #include <math.h>  
    #include <stdio.h>  
    #include <algorithm>  
    using namespace std;  
    struct Qujian  
    {  
        float a,b;  
    };  
    bool cmp(const Qujian& s1,const Qujian& s2)  
    {  
        return s1.a < s2.a;  
    }  
    int main()  
    {  
        Qujian A[10005];  
        int z,n,w,h,x,r,cnt;  
        float start,l,__h;  
        scanf("%d",&z);  
        while(z--)  
        {  
            scanf("%d%d%d",&n,&w,&h);  
            cnt = start = 0;  
            __h = h*h*1.0/4;  
            for(int i=0;i<n;i++)  
            {  
                scanf("%d%d",&x,&r);  
                if(r*r < __h)  
                {  
                    i--;n--;  
                    continue;  
                }  
                l = sqrt(r*r - __h);  
                A[i].a = x-l > 0 ? x-l : 0;  
                A[i].b = x+l < w ? x+l : w;  
            }  
            sort(A,A+n,cmp);  
            int i,k = -1;  
            while(start < w && A[k+1].a <= start)  
            {  
                float mmax = -1;  
                for(i = k+1;A[i].a <= start && i<n;i++)  
                    if(mmax < A[i].b)  
                    {  
                        mmax = A[i].b;  
                        k = i;  
                    }  
                start = mmax;  
                cnt++;  
            }  
            printf("%d\n",start<w ? 0 : cnt);  
        }  
        return 0;  
    }  
部分參考:http://blog.csdn.net/dgq8211/article/details/7535994