介绍几种贪心题型
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