zoukankan      html  css  js  c++  java
  • 贪心小总结

    介绍几种贪心题型

    1.选择不相交区间:

    按照结束时间从大到小排序,如果区间左端点大于当前最右点就选,否则不选。

    例题:活动安排:

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<queue>
    using namespace std;
    const int maxm=1100;
    int n,cnt;
    struct node
    {
      int l,r;
      bool operator < (const node&s) const
      {
       return r<s.r;	
      }
    }a[maxm];
    int main()
    {
     scanf("%d",&n);
     for(int i=1;i<=n;i++)
     {
      scanf("%d%d",&a[i].l,&a[i].r);	
     }
     sort(a+1,a+n+1);
     int tail=0;
     for(int i=1;i<=n;i++)
     {
       if(a[i].l>=tail)
       {
         tail=a[i].r;
         cnt++;
       }
     }
     printf("%d
    ",cnt);
     return 0;	
    }
    

    2.区间选点问题:

    按照区间的结束位置从大到小排序。对于当前区间如果选的点不够,就尽量在区间末尾选点。

    例题:种树

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<queue>
    using namespace std;
    int n,m;
    struct node
    {
     int l,r,t;
     bool operator < (const node &s) const
     {
      return r<s.r;	
     }
    }a[5100];
    bool flag[30007];
    int cnt;
    int main()
    {
     scanf("%d",&n);
     scanf("%d",&m);
     for(int i=1;i<=m;i++)
     {
      	scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].t);
     }
     sort(a+1,a+m+1);
     for(int i=1;i<=m;i++)
     {
       int k=0;
       for(int j=a[i].l;j<=a[i].r;j++)
       {
       	 if(flag[j])
       	 {
       	  k++;	
       	 }
       }
       if(k>=a[i].t) continue;
       for(int j=a[i].r;j>=a[i].l;j--)
       {
       	 if(!flag[j])
       	 {
       	 	cnt++;
       	 	k++;
       	 	flag[j]=1;
       	 }
       	 if(k==a[i].t)
       	 break;
       }
     }
     printf("%d
    ",cnt);
     return 0;	
    }
    

    3.区间覆盖问题:

    将闭区间按照左端点从小到大排序。对于当前区间,左端点大于已选区间最右端,记录个数。当前区间不满足此条件时,从这些满足条件的区间中,选择右端点最远的点,再判断此时的最右端能否覆盖当前区间的左端点。能,就重复上述过程,不能说明无法覆盖整个区间长。

    例题:喷水装置

    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<cstdio>
    #include<queue>
    using namespace std;
    const int maxm=15007;
    int t,n;
    double l,w,head,tail;
    struct node
    {
     double l,r;	
    }a[maxm];
    int tot;
    bool cmp(node a,node b)
    {
     return a.l<b.l;	
    }
    int main()
    {
     scanf("%d",&t);
     while(t--)
     {
      scanf("%d%lf%lf",&n,&l,&w);
      tot=0;
      for(int i=1;i<=n;i++)
      {
        double x,y;
        scanf("%lf%lf",&x,&y);
        if(y<=w*0.5) continue;
        double len=sqrt(y*y-(w*0.5)*(w*0.5));
        a[++tot].l=x-len;
        if(a[tot].l<(double)0) a[tot].l=0;
        a[tot].r=x+len;
      }
      sort(a+1,a+tot+1,cmp);
      if(a[1].l>(double)0)
      {
      	printf("-1
    ");
      	continue;
      }
      head=1;
      tail=(double)0;
      bool flag=1;
      int cnt=1,ans=0;
      for(int i=2;i<=tot;i++)
      {
       	if(a[i].l>tail)
       	{
       	   for(int j=head;j<=head+cnt-1;j++)
       	   {
       	   	  tail=max(a[j].r,tail);
       	   }
       	   if(tail<a[i].l)
       	   {
       	    flag=0;
    	    printf("-1
    ");
    	    break;
           }
           head=i;
           ans++;
           if(tail>=l)
           {
           	 flag=0;
           	 printf("%d
    ",ans);
           	 break;
           }
           cnt=1;
       	}
       	else if(a[i].l<=tail)
       	{
       	   cnt++;
       	}
      }
      for(int j=head;j<=head+cnt-1;j++)
      {
       tail=max(a[j].r,tail);
      }
      ans++;
      if(!flag) continue;
      if(tail<l){
        printf("-1
    ");
      	continue;	
      }
      printf("%d
    ",ans);
     }
     return 0;	
    }
    
    

    4.流水作业调度问题:

    题目概述:

    某工厂收到了n个产品的订单,这n个产品分别在A、B两个车间加工,并且必须先在A车间加工后才可以到B车间加工。某个产品在A、B两车间加工的时间分别为Ai、Bi。怎样安排这n个产品的加工顺序,才能使总的加工时间最短。这里所说的加工时间是指:从开始加工第一个产品到最后所有的产品都已在A,B两车间加工完毕的时间。

    做法:Johnson算法。要使机器总的空闲时间最短,就要把在A上加工时间最短的部件先加工,这样B能在最短的空闲时间后开始工作;把在B上加工时间最短的部件放在最后加工,使得A机器用最短的时间等待B机器完工。

    设Mi = min(ai , bi).

    将M按照从大到小的顺序排序进行处理。对于Mi=ai,则将它排在从头开始的作业后面,若Mi = bi,则将它排在从尾部开始的作业前面.算法正确性不太会证,可自行查阅。

    例题:加工生产调度

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<queue>
    using namespace std;
    const int maxm=1100;
    int n;
    struct node
    {
     int t1,t2;
     int id,m;
    }a[maxm];
    bool cmp(node a,node b)
    {
     return a.m<b.m;	
    }
    int q[maxm],l,r;
    int A[maxm],B[maxm];
    int main()
    {
     scanf("%d",&n);
     for(int i=1;i<=n;i++)
     scanf("%d",&a[i].t1),A[i]=a[i].t1;
     for(int i=1;i<=n;i++)
     scanf("%d",&a[i].t2),B[i]=a[i].t2;
     for(int i=1;i<=n;i++)
     {
     	a[i].id=i;
     	a[i].m=min(a[i].t1,a[i].t2);
     }
     sort(a+1,a+n+1,cmp);
     l=0;
     r=n+1;
     for(int i=1;i<=n;i++)
     {
      	if(a[i].m==a[i].t1)
      	{
      	  q[++l]=a[i].id;
      	}
      	else
      	{
    	  q[--r]=a[i].id;
        }
     }
     int ans1=0,ans2=0;
     for(int i=1;i<=n;i++)
     {
       	ans1+=A[q[i]];
       	if(ans2<ans1) ans2=ans1;
       	ans2+=B[q[i]];
     }
     printf("%d
    ",ans2);
     for(int i=1;i<=n;i++)
     {
      if(i!=n)
      printf("%d ",q[i]);
      else
      printf("%d",q[i]);
     }
     return 0;	
    }
    

    5.带限期和罚款(得分)的单位时间任务调度

    将罚款(得分)按从大到小排序处理。将当前任务放在既在期限之内,又尽量靠后的时间完成。如果不能再期限完成,根据题意如果是必须完成,就放到整个时间段尽量靠后的位置,此位置打标记。

    例题:智力大冲浪

    #include<cstring>
    #include<algorithm>
    #include<cstdio>
    #include<cmath>
    #include<iostream>
    using namespace std;
    const int maxm=510;
    int m,n;
    struct node
    {
     int w,t;
     bool operator <(const node &s) const
     {
      return w>s.w;
     }
    }a[maxm];
    bool flag[maxm];
    int main()
    {
     scanf("%d",&m);
     scanf("%d",&n);
     for(int i=1;i<=n;i++)
     scanf("%d",&a[i].t);
     for(int i=1;i<=n;i++)
     scanf("%d",&a[i].w);
     sort(a+1,a+n+1);
     for(int i=1;i<=n;i++)
     {
       bool mon=1;
       for(int j=a[i].t;j>=1;j--)
       {
       	 if(flag[j]) continue;
       	 flag[j]=1;
       	 mon=0;
       	 break;
       }
       if(mon)
       {
       	 for(int j=n;j>=1;j--)
       	 {
       	  if(flag[j]) continue;
    		 flag[j]=1;
    		 m-=a[i].w;
    		 break;
       	 }
       }
     }
     printf("%d
    ",m);
     return 0;	
    }
    

    6.均分纸牌问题

    1.均分纸牌通用公式:({T})为总和,(ave=frac{T}{N})({S})是纸牌数组({a[i]-ave})的前缀和,(ans=sum_{i=1}^{n}left | S[i] ight |)

    2.环形均分纸牌:在第K个人之后把环断开站成一行,这N个人持有的纸牌数、前缀和分别是

    A[K+1]、S[K+1]-S[K],A[K+2]、S[K+2]-S[K]...A[N]、S[N]-S[K],A[1]、S[1]+S[N]-S[K]...A[K]、S[K]+S[N]-S[K] 。

    由于均分之后,({S[M]=0})恒成立,所以前缀和的变化仅仅是减去({S[k]})

    所以,所需最小花费:(sum_{i=1}^{n}left | S[i]-S[k] ight |)

    当K取何值时上式最小?这就是“货仓选址”问题(在数轴上找一点使得该点到所有其他点的距离之和最小)法:找到大小为中位数的点,该点就是要求的点(如有两个取之间任意一点都行)

    例题:[HAOI2008]糖果传递

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<queue>
    using namespace std;
    typedef long long ll;
    const int maxm=1e6+7;
    int n;
    ll sum[maxm],a[maxm],ave;
    ll ans;
    int main()
    {
     scanf("%d",&n);
     for(int i=1;i<=n;i++)
     scanf("%lld",a+i),ave+=a[i];
     ave/=n;
     for(int i=1;i<=n;i++)
     {
      a[i]-=ave;
      sum[i]=sum[i-1]+a[i];
     }
     sort(sum+1,sum+n+1);
     int id=(n+1)/2;
     for(int i=1;i<=n;i++)
     {
      ans+=abs(sum[i]-sum[id]);
     }
     printf("%lld
    ",ans);
     return 0;	
    } 
    

    练习题:

    1.数列极差:维护一个大根堆和一个小根堆,大根堆取出两个数合并,最后算出来的数最小;小根堆取出两个数合并,最后得出最大值。

    2.数列分段:直接贪心即可,超过({M})答案就加({1})

    3.线段:模型1。

    4.家庭作业:模型5,因为({n,t})较大,可以进行剪枝。新开一个({vis}),表示({1-vis})已经全部占完。当期限小于它时,就直接舍去。(注意本题无要求必须做完)

    5.钓鱼:借助DP思想,枚举以i池塘结束的最大钓鱼数。先预处理({t[i]})表示({1-i})池塘行走的时间.在进行枚举时,找出此次钓鱼数最多的池塘,给({sum})加上,在把池塘的鱼对应减少。当最大值为({0})时或超过时间范围,跳出与({ans})比较。

    #include <cstdio>
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cmath>
    #include <queue>
    using namespace std;
    const int maxm=110;
    int n,h,ans;
    struct node
    {
     int start,cos;
     int t;	
    }a[maxm];
    int b[maxm];
    int t[maxm];//记录路程
    int t1,sum;
    int find(int x)
    {
      int id,maxx=-1;
      for(int i=1;i<=x;i++)
      {
        if(maxx<b[i])
        maxx=b[i],id=i;	
      }
      return id;
    }
    int main()
    {
     scanf("%d",&n);
     scanf("%d",&h);
     h*=60;
     for(int i=1;i<=n;i++)
     scanf("%d",&a[i].start);
     for(int i=1;i<=n;i++)
     scanf("%d",&a[i].cos);
     for(int i=1;i<=n-1;i++)
     scanf("%d",&a[i].t);
     for(int i=1;i<=n;i++)
     t[i]=t[i-1]+a[i-1].t*5;
     for(int i=1;i<=n;i++)//枚举以i为终点
     {
       t1=t[i];
       sum=0;
       for(int j=1;j<=n;j++)
       b[j]=a[j].start;
       for(int j=i;;)
       {
       	 int bj=find(j);
       	 if(b[bj]<=0) break;
       	 sum+=b[bj];
       	 b[bj]-=a[bj].cos;
       	 t1+=5;
       	 if(t1+5>h) break;
       }
       ans=max(ans,sum); 
     }
     printf("%d
    ",ans);
     return 0;	
    }
    
    
  • 相关阅读:
    Integer类的parseInt和valueOf的区别
    华为实习小结
    程序员浪费生命的几种方式
    移动前端中viewport(视口) 转
    Console API 与命令行
    Ajax
    浏览器缓存机制
    mysql之各种命令总结
    jquery file upload 文件上传插件
    文件上传插件uploadify详解
  • 原文地址:https://www.cnblogs.com/lihan123/p/11663479.html
Copyright © 2011-2022 走看看