贪心

1422:【例题1】活动安排

将终点进行一个排序之后来进行贪心的计算

【题目描述】
设有nn个活动的集合E={1,2,…,n}E={1,2,…,n},其中每个活动都要求使用同一资源,如演讲会场等,而在同一时间内只有一个活动能使用这一资源。每个活动ii都有一个要求使用该资源的起始时间sisi和一个结束时间fifi,且si<fisi<fi。如果选择了活动ii,则它在半开时间区间[si,fi)[si,fi)内占用资源。若区间[si,fi)[si,fi)与区间[sj,fj)[sj,fj)不相交,则称活动ii与活动jj是相容的。也就是说,当si≥fjsi≥fj或sj≥fisj≥fi时,活动ii与活动jj相容。选择出由相互兼容的活动组成的最大集合。

【输入】
第11行一个整数n(n≤1000)n(n≤1000),接下来nn行,每行两个整数sisi和fifi。

【输出】
输出尽可能多的互相兼容的活动个数。

【输入样例】
4
1 3
4 6
2 5
1 7
【输出样例】
2
1422:【例题1】活动安排
#include <bits/stdc++.h>
using namespace std;
const int N=1005;
struct h{
    int en;
    int be;
}a[1005];
bool cmp(h a,h b)
{
    return a.en<b.en;
}
int main()
{
    int n;
    int sum=0;
    int endd=-1;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i].be>>a[i].en;
    }
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++)
    {
        if(a[i].be>=endd)
        {
            sum++;
            endd=a[i].en;
        }
    }
    cout<<sum<<endl;
    return 0;
}
View Code

1423:【例题2】种树

同样也是将每一个区域的重点进行一个排序,先是从前往后看前面是否有重合的已经种了树的,如果该区域还没有达到要求的话,就从后面开始种树,因为尾部是最容易有重合的地方

【题目描述】
现在我们国家开展新农村建设,农村的住房建设纳入了统一规划,统一建设,政府要求每一住户门口种些树。门口路边的地区被分割成块,并被编号成1..N。每个部分为一个单位尺寸大小并最多可种一棵树。每个居民房子门前被指定了三个号码B,E,T。这三个数表示该居民想在B和E之间最少种T棵树。当然,B≤E,居民必须记住在指定区不能种多于区域地块数的树,所以T≤E-B+l。居民们想种树的各自区域可以交叉。你的任务是求出能满足所有要求的最少的树的数量,尽量较少政府的支出。

【输入】
第一行包含数据N,M,区域的个数(0<N≤30000),房子的数目(0<m≤5000);

下面的m行描述居民们的需要:B E T,0<B≤E≤30000,T≤E-B+1。

【输出】
输出一个数,为满足所有居民的要求,所需要种树的最少数量。

【输入样例】
9 4
3 5 2
1 4 2
4 6 2
8 9 2
【输出样例】
5
种树
#include <bits/stdc++.h>
using namespace std;
const int N=30005,M=5005,inf=0x3f3f3f;
struct line{
    int s,e,num;
}a[N];
int vis[N]; 
bool cmp(line x,line y){
    return x.e<y.e;
}
int main()
{
    int n,m,ans=0,k;
    cin>>n>>m;
    for(int i=1;i<=m;i++) cin>>a[i].s>>a[i].e>>a[i].num;
    sort(a+1,a+1+m,cmp);
    for(int i=1;i<=m;i++){
        k=0;
        for(int j=a[i].s;j<=a[i].e;j++)
            if(vis[j]) k++;//记录已经种了树的区域
        if(k>=a[i].num) continue;//已经满足此区域的要求
        for(int j=a[i].e;j>=a[i].s;j--){//从尾巴是最有可能和下一个区域重合的 
            if(!vis[j]){
            vis[j]=1;k++;ans++;    
            if(k==a[i].num) break;
            }    
        } 
    }
    cout<<ans;
    return 0;
}
View Code

1424:【例题3】喷水装置

【题目描述】
长 LL 米,宽 WW 米的草坪里装有 nn 个浇灌喷头。每个喷头都装在草坪中心线上(离两边各 W2W2 米)。我们知道每个喷头的位置(离草坪中心线左端的距离),以及它能覆盖到的浇灌范围。



请问:如果要同时浇灌整块草坪,最少需要打开多少个喷头?

【输入】
输入包含若干组测试数据。

第一行一个整数 TT 表示数据组数;

每组数据的第一行是整数 nn、LL 和 WW;

接下来的 nn 行,每行包含两个整数,给出一个喷头的位置和浇灌半径(上面的示意图是样例输入第一组数据所描述的情况)。

【输出】
对每组测试数据输出一个数字,表示要浇灌整块草坪所需喷头数目的最小值。如果所有喷头都打开也不能浇灌整块草坪,则输出 −11 。

【输入样例】
3
8 20 2
5 3
4 1
1 2
7 2
10 2
13 3
16 2
19 4
3 10 1
3 5
9 3
6 1
3 10 1
5 3
1 1
9 1
【输出样例】
6
2
-1
【提示】
数据范围:

对于 100% 的数据,n≤15000
喷水装置

 其实有用到勾股定理,自己画图理解下,然后按照起点从小到大排序,先找在起点在草坪左端的,谁的右边最大,依次这样下去;如果没有就在草坪右边的就不行,最后求出的右端点小于草坪的长度也是不行

#include <bits/stdc++.h>
using namespace std;
const int N=20005,inf=0x3f3f3f;
struct line{
    double s,e;
}a[N]; 
int n,l,w,x,r,cnt;
bool cmp(line x,line y)
{
    return x.s<y.s;
}
void init()
{
    cnt=0;
    cin>>n>>l>>w;
    for(int i=1;i<=n;i++)
    {
        cin>>x>>r;
        if(r<=w/2) continue;
        cnt++;
        a[cnt].s=x-sqrt(r*r-w*w/4.0);
        a[cnt].e=x+sqrt(r*r-w*w/4.0);
    }
}
void solve()
{
    double t=0;
    int ans=0,bj=1,i=1;
    while(t<l){
        ans++;
        double s=t;
        for(;a[i].s<=s&&i<=cnt;i++)//找到能够覆盖左端点的最靠近右边的点 
            if(t<a[i].e) t=a[i].e;
        if(t==s&&s<l){
            cout<<-1<<endl;
            bj=0;break;
        } 
    }
    if(bj) cout<<ans<<endl;
}
int main()
{
    int t;cin>>t;
    while(t--){
        init();
        sort(a+1,a+1+cnt,cmp);
        solve();
    }
    return 0;
}
View Code

1425:【例题4】加工生产调度

【题目描述】
某工厂收到了 nn 个产品的订单,这 nn 个产品分别在 A、B 两个车间加工,并且必须先在 A 车间加工后才可以到 B 车间加工。

某个产品 ii 在 A,B 两车间加工的时间分别为Ai,BiAi,Bi。怎样安排这 nn 个产品的加工顺序,才能使总的加工时间最短。

这里所说的加工时间是指:从开始加工第一个产品到最后所有的产品都已在 A,B 两车间加工完毕的时间。

【输入】
第一行仅—个数据 nn ,表示产品的数量;

接下来 nn 个数据是表示这 nn 个产品在 A 车间加工各自所要的时间;

最后的 nn 个数据是表示这 nn 个产品在 B 车间加工各自所要的时间。

【输出】
第一行一个数据,表示最少的加工时间;

第二行是一种最小加工时间的加工顺序。

【输入样例】
5
3 5 8 7 10
6 2 1 4 9
【输出样例】
34
1 5 4 2 3
【提示】
对于100%的数据, 0 < n < 10000,所有数值皆为整数。
1425:【例题4】加工生产调度

Johnson算法:设N1为a<b的作业集合,N2为a>=b的作业集合,将N1的作业按a非降序排序,N2的作业按b非增序排序,则N1作业接N2作业构成最优顺序。

这个题目的思路:我们第一件物品加工b机器肯定会等,所以就让a机器开始加工的物品时间最短,而我们B机器最后加工完成肯定是A机器在等,所以让B机器最后加工完的时间尽可能短

#include <bits/stdc++.h>
using namespace std;
const int N=10005;
int a[N],b[N],s[N],m[N],ans[N],n,k,t; 
void solve()
{
    for(int i=1;i<=n;i++){m[i]=min(a[i],b[i]);s[i]=i;}
    for(int i=1;i<=n-1;i++)
        for(int j=i+1;j<=n;j++)
            if(m[i]>m[j]){swap(m[i],m[j]);swap(s[i],s[j]);}
    k=0;t=n+1;//k为A加工时间,t为B加工时间
    for(int i=1;i<=n;i++)
        if(m[i]==a[s[i]]){k++;ans[k]=s[i];}//就是N1类的,a<b
        else{t--;ans[t]=s[i];} //这种就是N2的,从后往前来
    k=0;t=0;
    for(int i=1;i<=n;i++)
    {
        k+=a[ans[i]];
        if(t<k) t=k;
        t+=b[ans[i]];
    } 
    cout<<t<<endl;//B机器结束的时间是结束的时间
    for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
    cout<<endl;
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++) cin>>b[i];
    solve();
    return 0;
}
/*把所有任务分为两类
1类ai<bi,2类ai>bi
至于ai=bi放在哪一类均可,无影响,
1类可按ai排序,ai小的靠前 
2类按bi排序,bi小的靠后 
安排后的结果一定是1类全部执行完再执行2类*/
View Code

1426:【例题5】智力大冲浪

【题目描述】
小伟报名参加中央电视台的智力大冲浪节目。本次挑战赛吸引了众多参赛者,主持人为了表彰大家的勇气,先奖励每个参赛者m元。先不要太高兴!因为这些钱还不一定都是你的。接下来主持人宣布了比赛规则: 首先,比赛时间分为n个时段(n≤500),它又给出了很多小游戏,每个小游戏都必须在规定期限ti前完成(1≤ti≤n)。如果一个游戏没能在规定期限前完成,则要从奖励费m元中扣去一部分钱wi,wi为自然数,不同的游戏扣去的钱是不一样的。当然,每个游戏本身都很简单,保证每个参赛者都能在一个时段内完成,而且都必须从整时段开始。主持人只是想考考每个参赛者如何安排组织自己做游戏的顺序。作为参赛者,小伟很想赢得冠军,当然更想赢取最多的钱! 注意:比赛绝对不会让参赛者赔钱!

【输入】
输入共4行。

第一行为m,表示一开始奖励给每位参赛者的钱;

第二行为n,表示有n个小游戏; 第三行有n个数,分别表示游戏1~n的规定完成期限;

第四行有n个数,分别表示游戏1~n不能在规定期限前完成的扣款数。

【输出】
仅1行。表示小伟能赢取最多的钱。

【输入样例】
10000
7
4 2 4 3 1 4 6
70 60 50 40 30 20 10
【输出样例】
9950
【提示】
数据范围及提示:

n≤5001≤ti≤n
1426:【例题5】智力大冲浪

思路:

先按罚款数额从大到小快排;顺序处理每个任务,若能安排,则找一个最晚时间,否则放在最后的空位上。

#include <bits/stdc++.h>
using namespace std;
struct th
{
    int a,b;
}t[505];
int pd[505];
bool cmp(th x,th y)
{
    return x.b>y.b;
}
int main()
{
    int n,m,i,j;
    cin>>m;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>t[i].a;
    }
    for(int i=1;i<=n;i++)
    {
        cin>>t[i].b;
    }
    sort(t+1,t+1+n,cmp);
    for(i=1;i<=n;i++)
    {
        for(j=t[i].a;j>=1;j--)
        {
            if(pd[j]==0)
            {
                pd[j]=1;
                break;
            }
        }
        if(j==0)
            m-=t[i].b;
    }
    cout<<m<<endl;
    return 0;
}
View Code

1427:数列极差

思路:从小到大来的话就是最大值,从大到小来就是最小值;例如 1 2 3 ,(1*2+1)*3+1=10;(3*2+1)*1+1=8

#include <bits/stdc++.h>
using namespace std;
int n,a[50005],ans,mod;
priority_queue<int,vector<int>,greater<int> > q;//小根堆
bool cmp(int a,int b){
    return a>b;
}
int findmax()
{
    int temp;
    while(!q.empty())
    {
        temp=q.top();
        q.pop();
        temp=(temp*q.top()+1)%mod;
        q.pop();
        if(q.empty()) break;
        q.push(temp);
    }
    return temp;
}
int findmin() 
{
    sort(a+1,a+1+n,cmp);
    int ans=a[1];
    for(int i=2;i<=n;i++)
        ans=(ans*a[i]+1)%mod;
    return ans;
}
int main()
{
    cin>>n>>mod;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        q.push(a[i]);
    }
    int maxx=findmax();
    int minn=findmin();
    cout<<(maxx-minn)%mod<<endl;
    return 0;
}
View Code

1428:数列分段

#include <bits/stdc++.h>
using namespace std;
int a[100005];
int main()
{
    int n,m,i,sum=0,ans=1;
    cin>>n>>m;
    for(i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    for(i=1;i<=n;i++)
    {
        if(sum+a[i]>m)
        {
            ans++;
            sum=0;
        }
        sum+=a[i];
    }
    cout<<ans<<endl;
    return 0;
}
View Code

1429:线段

思路:按着段尾从小到大进行排序,在来比较已经被选择的上一段的队尾和队首来进行比较就可以了

#include <bits/stdc++.h>
using namespace std;
struct line
{
    int a,b;
}l[1000005];
bool cmp(line x,line y)
{
    return x.b<y.b;
}
int main()
{
    int n,ans=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d %d",&l[i].a,&l[i].b);
    }
    sort(l+1,l+1+n,cmp);//按着段尾从小到大进行排序;
    int e=-1;
    for(int i=1;i<=n;i++)
    {
        if(l[i].a>=e)
        {
            ans++;
            e=l[i].b;
        }
     } 
     printf("%d\n",ans);
    return 0;
}
View Code

1430:家庭作业

其实还是有点难,是一个贪心加上并查集的题目:根据学分从大到小进行排序,然后是进行一些并查集的操作,就看上一个时间

#include<bits/stdc++.h>
using namespace std;
const int N=1000005;
struct node{
    int t,s;
}stu[N];
int n,flag,fa[N]; 
bool cmp(node x,node y){
    if(x.s==y.s) return x.t<y.t;
    return x.s>y.s;
}
int find(int x){
    if(x<=0) return x;
    else if(x==fa[x]){
        fa[x]=x-1;
        flag=1;
        return fa[x];
    }
    else return fa[x]=find(fa[x]);
}
int main() {
    cin>>n;
    for(int i=1;i<=n;i++) scanf("%d %d",&stu[i].t,&stu[i].s);
    sort(stu+1,stu+1+n,cmp);
    for(int i=1;i<=n;i++) fa[i]=i;
    long long ans=0;
    for(int i=1;i<=n;i++){
        flag=0;
        fa[stu[i].t]=find(stu[i].t);
        if(flag) ans+=stu[i].s;
    }
    printf("%d\n",ans);
    return 0;
}
/*
7
4 0
4 5 7
0
5 6 0
1 0
1 0
7 0
0
5 
2 5 0
0 
2 4 0
5 0
0
*/
View Code

1431:钓鱼

这个题目要用优先队列来做,要用到重载函数,主要的思路其实还是去枚举每个鱼塘作为最后一个鱼塘,那么根据贪心的思想,这个题目是肯定不会回头的,那么确定最后一个鱼塘之后,就按着最大的来,这个顺序不是最终的顺序,但是一定是把时间范围内能钓的最多的鱼都算进去了

比如说     4 (1) 5(2) 6(1)     走路需要3个5分钟,一共12个5分钟,钓鱼的时间为9个五分钟

1:6   还剩 4 5 5   2:5  4 5 4   3 5   4 3 4     ...这个不是钓鱼顺序

#include <bits/stdc++.h>
using namespace std;
const int N=10005,inf=0x3f3f3f;
struct node{
    int num,lose;
    bool operator <(node x)const{
        return num<x.num;
    }
}a[N]; 
int t[N],n,h,ans=0;
priority_queue<node> q;
int main()
{
    cin>>n>>h;
    h*=12;//以5分钟为界限
    for(int i=1;i<=n;i++){ cin>>a[i].num;}
    for(int i=1;i<=n;i++){cin>>a[i].lose;}
    for(int i=1;i<=n-1;i++) cin>>t[i]; 
    for(int i=1;i<=n;i++){//枚举每个鱼塘作为最后一个 
        h-=t[i-1];
        int now=0;
        while(!q.empty()) q.pop(); //初始化
        for(int j=1;j<=i;j++) q.push(a[j]);//把以i为最后一个鱼塘的所有鱼塘加入
        for(int j=1;j<=h;j++)
        {
            node s=q.top();
            cout<<s.num<<endl;
            if(s.num>0) now+=s.num;
            s.num-=s.lose;
            q.pop();
            q.push(s);
        } 
        ans=max(now,ans);
    }
    cout<<ans<<endl;
    return 0;
}
View Code

 

1432:糖果传递

这个题目是一个数学的推导题:我们先求出最终每个人的平均值avr,直接给的就只有旁边两个人,我们统一一下来进行假设,给左边,从右边拿

avr=a[1]-x1+x2    x2=avr-a[1]+x1   x2是右边给的,那么对于右边的小朋友来说,就是给左边的,所以

avr=a[2]-x2+x3   x3=avr-a[2]+x2=avr-a[2]+avr-a[1]+x1

...

avr=a[n]-xn+x1   无用

用一个数组c[]表示 a[]-avr

那么  x2=x1-c[1]   x3=x1-c[2]...c[2]=c[1]+a[]-avr;

最后就是求x1+x2+x3+x4+...+xn

又因为:x1+x1-c1+x2-c2+...+xn-1-cn-1

我们希望Xi的绝对值之和尽量小,即|X1| + |X1-C1| + |X1-C2| + ……+ |X1-Cn-1|要尽量小。注意到|X1-Ci|的几何意义是数轴上的点X1到Ci的距离,所以问题变成了:给定数轴上的n个点,找出一个到他们的距离之和尽量小的点,而这个点就是这些数中的中位数

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1000005,inf=0x3f3f3f;
ll a[N],c[N],ans=0,n,sum=0,mid,avr;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++){scanf("%lld",&a[i]);sum+=a[i];}
    avr=sum/n;
    for(int i=1;i<=n;i++){c[i]=c[i-1]+a[i]-avr;}
    sort(c,c+n);
    if(n&1){
        mid=c[n/2];
    }
    else{
        mid=(c[(n-1)/2]+c[(n-1)/2+1])/2;
    }//找中位数 
    for(int i=0;i<n;i++)
        ans+=abs(c[i]-mid); 
    printf("%lld\n",ans);
    return 0;
}
View Code

 

posted @ 2020-05-08 20:12  sumoier  阅读(516)  评论(0编辑  收藏  举报