贪心小总结

介绍几种贪心题型

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\n",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\n",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\n");
    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\n");
        break;
       }
       head=i;
       ans++;
       if(tail>=l)
       {
         flag=0;
         printf("%d\n",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\n");
    continue;
  }
  printf("%d\n",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\n",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\n",m);
 return 0;
}

6.均分纸牌问题

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

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] \right |\)。

当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\n",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\n",ans);
 return 0;
}

原文地址:https://www.cnblogs.com/lihan123/p/11663479.html

时间: 2024-11-09 06:15:17

贪心小总结的相关文章

A. Ilya and Diplomas 贪心小题

A. Ilya and Diplomas 题目抽象:min1<=a<=max1,   min2 <= b <= max2, min3 <= c <= max3;   a+b+c = n. min1 +min2 + min3 <= n <= max1 + max2 + max3; 在满足上面的条件下,第一比较规则a尽可能大,第二比较规则b尽可能大.求出a,b,c; 分析: 贪心即可.分两类, a能否取到最大值,   a取到最大值的话,b能否取到最大值.  见代

[BZOJ1193][HNOI2006]马步距离 大范围贪心小范围爆搜

1193: [HNOI2006]马步距离 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1988  Solved: 905[Submit][Status][Discuss] Description 在国际象棋和中国象棋中,马的移动规则相同,都是走"日"字,我们将这种移动方式称为马步移动.如图所示, 从标号为 0 的点出发,可以经过一步马步移动达到标号为 1 的点,经过两步马步移动达到标号为 2 的点.任给 平面上的两点 p 和 s ,它

贪心(bnuoj49103+二分+贪心)

贪心 小明喜欢养小鸡,小鸡喜欢吃小米.小明很贪心,希望养s只不同种类的小鸡,小鸡也很贪心,每天除了吃固定的ai粒小米外,还想多吃bi*s粒小米. 小明每天有M(0<=M<=10^9)粒小米可以喂小鸡,小鸡共有N(0<=N<=1000)种.问小明最多可以养多少只小鸡? Input 多组数据,请读到文件尾 第一行,整数N,M,以空格分隔,之后两行,第一行为N个整数ai,第二行为N个整数bi. ai.bi都在int范围内 Output 一行一个整数,s. Sample Input 2 4

Good Bye 2014

A:签到,从左往右走一遍判断下有没有遇到t即可 B:先利用floyd求出传递闭包,然后利用这个传递闭包贪心小的尽量往前放即可 C:贪心的策略,放的顺序其实根据拿的顺序就可以确定的,所以只要在拿的顺序上从左往右扫一遍即可 D:先DFS预处理出每条边两边点的个数,然后三元组对于每个边经过都是n - 2次,所以一个边都会被计算到n - 2 * 一边点 * 另一边点个数 代码: #include <cstdio> #include <cstdlib> const int N = 30005

zoj 3715 K - Kindergarten Election

一个班有n个小朋友,要选一个班长,(n>=3),每个人不能投自己,给出每个人想要选的人,1号小朋友相当班长,如果一个小朋友不选自己 那么,自己可以给他bi个糖果让他选自己,那么请输出,最少花费多少个糖果 ,1号小朋友可以当上班长 开始的做法是贪心小的,那么问题很复杂,直接枚举小的是不符合条件的 后来想,枚举一号小朋友最后的票为k,那么其他人的票肯定小于等于k-1,如果有小朋友的选票高于k-1,那么先把投这个人的所有小朋友贿赂到小于k-1,按照需要的糖果数量 如果这个时候的选票大于k了,那么显然这

【BZOJ 1193】 [HNOI2006]马步距离

1193: [HNOI2006]马步距离 Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 919  Solved: 419 [Submit][Status] Description Input 只包含4个整数,它们彼此用空格隔开,分别为xp,yp,xs,ys.并且它们的都小于10000000. Output 含一个整数,表示从点p到点s至少需要经过的马步移动次数. Sample Input 1 2 7 9 Sample Output 5 贪心+暴

POJ3253 Fence Repair 小顶堆+贪心

给了你N个木棒,求把他们组装成一根需要的最小花费,每次只能选两根组装在一起,需要的花费为两个木棒之和, 以前遇到过把一整根切开的,那个是DP,这个则有些类似,可是大胆的猜测了一下,直接每次选取所有木棒中最短的两根,这样就可以了,那么贪心是适用的,但是数量很多,而且两根最短的组装好了得插回去,这样不可能每次都排序吧, 这题首先优先队列肯定是可以做的, 最小堆也是可以的,每次都选出堆里的最小的两个求和再放回去即可 队列本身也就是堆吧,所以差别不大,但是没用过最小堆最大堆的 所以用一次把 #inclu

【bzoj4813】[Cqoi2017]小Q的棋盘 dfs+贪心

题目描述 小Q正在设计一种棋类游戏.在小Q设计的游戏中,棋子可以放在棋盘上的格点中.某些格点之间有连线,棋子只能在有连线的格点之间移动.整个棋盘上共有V个格点,编号为0,1,2-,V-1,它们是连通的,也就是说棋子从任意格点出发,总能到达所有的格点.小Q在设计棋盘时,还保证棋子从一个格点移动到另外任一格点的路径是唯一的.小Q现在想知道,当棋子从格点0出发,移动N步最多能经过多少格点.格点可以重复经过多次,但不重复计数. 输入 第一行包含2个正整数V,N,其中V表示格点总数,N表示移动步数. 接下

nyoj 236心急的C小加(贪心)

心急的C小加 时间限制:1000 ms  |  内存限制:65535 KB 难度:4 描述 C小加有一些木棒,它们的长度和质量都已经知道,需要一个机器处理这些木棒,机器开启的时候需要耗费一个单位的时间,如果第i+1个木棒的重量和长度都大于等于第i个处理的木棒,那么将不会耗费时间,否则需要消耗一个单位的时间.因为急着去约会,C小加想在最短的时间内把木棒处理完,你能告诉他应该怎样做吗? 输入 第一行是一个整数T(1<T<1500),表示输入数据一共有T组. 每组测试数据的第一行是一个整数N(1&l