算法笔记#2:校队公开赛中几道经典贪心算法题

写在最前:

        高中学信息技术或者搞过信奥的可能对这方面已经有些基础,但很可惜本人并没有这方面的基础。这半个月时间也是初次涉猎。这里放的只是目前遇到的一部分,在后面还会继续补题。题目均来自TZOJ。

        大致分为两个内容:来自公开赛的题目和和作业里的部分题目以及类似题。每一部分的内容布置是按照难度循序渐进的,刚开始几道不是很复杂的题目我都用c来写,后面几道用c++。因为过程一复杂再用c的话,代码就会显得有些重复累赘了。


11.14集训队简单贪心专题(部分题目)

1001 心疼giegie

描述

哥哥,你骑着小,小电动车,载~着我,你女朋友知道了,不会揍我吧!

好(浩)可(克)怕你女朋友~

不像我,我只会心疼giegie~

这个视频大家都看过吧!

现在,这个giegie要给这个女的买n件礼物。

giegie有x元,每买一件礼物,这个giegie的危险程度就是这个的金额的50%,输出giegie最多可以给那个“绿茶”几件礼物(在危险值不大于500的情况下)。

输入

第一行两个正整数:n(0<=n<=100)表示看中了n件商品,x(0<=x<=10000)表示giegie有几块钱。

下面n行,每行一个实数,表示每件礼物的价格,不超过500。

输出

输出giegie最多可以买到的礼物数量。

样例输入

2 1546
271
154

样例输出

2

签到题。从小到大排序之后,从小的开始买才可以达到目的。

#include<stdio.h>
#define maxn 1001
void sort(int a[],int n)
{
    int i,j,t;
    for(i=1;i<n;i++)
    {
        for(j=0;j<n-1;j++)
        {
            if(a[j]>a[j+1])
            {
                t=a[j];
                a[j]=a[j+1];
                a[j+1]=t;
            }
        }
    }
}
int main()
{
    int a[maxn],n,x,i,j,k,s=0,sum=0,warn=0;
    scanf("%d%d",&n,&x);
    for(i=0;i<n;i++)
    {
        scanf("%d",&a[i]);
    }
    sort(a,n);
    for(i=0;i<n;i++)
    {
        sum+=a[i];
        warn+=a[i]*0.5;
        if(sum<=x&&warn<=500)
        s+=1;


    }
    printf("%d",s);
    return 0;


}

1002 整数区间

描述

请编程完成以下任务:

1.从文件中读取闭区间的个数及它们的描述;

2.找到一个含元素个数最少的集合,使得对于每一个区间,都至少有一个整数属于该集合,输出该集合的元素个数。

输入

首行包括区间的数目n,1≤n≤10000,接下来的n行,每行包括两个整数a,b,被一空格隔开,0≤a≤b≤10000,它们是某一个区间的开始值和结束值。

输出

输出集合元素的个数, 对于每一个区间都至少有一个整数属于该集合, 且集合所包含元素数目最少。

样例输入

4
3 6
2 4
0 2
4 7

样例输出

2

先审题。我画了一张图,题目的意思大致如下:

 解析:

          对于区间问题,区间的起点和终点是有内在联系的,而且在众多组数据中,仅仅是这两个数据之间存在联系,因此需要用到结构体。

         在这里,sum表示的是题目要求的数组中元素的个数。在逐个输入每一个区间范围之后,对每个区间的右端点进行从小到大的排序。排序完成后,每次选取其中一个右端点将右端点和其他的左端点进行比较,如果说比较结果是大于,说明两个区间有元素重合,若小于则必不可能有元素重合。那么遇到重合的情况则sum=1即可,如果遇到不重合的情况则sum需要多一个元素来搭建一个“桥”。比如我上面的这张图,对于【0,2】这个区间,需要4这个元素,来和其他三个区间建立起联系,4就是一座桥。但是你不需要知道这座“桥”具体是什么数,因此直接让sum++。

        因为右端点是从小到大顺序排的,既然出现了不重合的情况,并且“桥”已经搭起来了,如果说题目并没有说明“要求数组元素个数最小”,你依然可以用这个右端点继续比下去。但既然题目规定了,那就得让第二小的右端点进行下一轮比较,可以节约搭“桥”的数量。

#include<stdio.h>
#define maxn 10001
struct node{
	int a;
	int b;
}z[maxn],t;
int main(){
	int n;
	scanf("%d",&n);
	int i,j;
	for(i=1;i<=n;i++){
		scanf("%d%d",&z[i].a,&z[i].b);
	}
	for(i=1;i<=n;i++){
		for(j=i+1;j<=n;j++){
			if(z[i].b>z[j].b){
				t=z[i];
				z[i]=z[j];
				z[j]=t;
			}
		}
	}
	
	int temp=z[1].b;
	int sum=1;
	for(i=2;i<=n;i++){
		if(temp<z[i].a){
			sum++;
			temp=z[i].b;
		}
	}
	printf("%d",sum);
	return 0;
} 

1003 小船过河

描述

N个人要过河,但只有一条小船,每次只能坐2人,每个人有不同的划船速度,而两个人一起过河时小船速度由最慢的人的速度决定。请设计一个过河方案,使得所有人均过河,且所用总时间最少。

输入

第一行为正整数N(N<=1000),表示人数,第二行为N个正整数,表示每个人的速度(这里是时间值,值越小速度越快),不超过100。

输出

输出花费最少的总时间。

样例输入

4
1 2 5 10

样例输出

17

一开始做的时候没有想到要分类讨论,吃了几次亏之后才发现,运人有两种方法(在这里,速度最快的假设为1,速度第二快的假设为2,速度最慢的设为99,速度第二慢的设为98,以此类推):

方法一:

让1做摆渡人,来回都有他。每次1载一名速度大的,直到运完。

方法二:

第一趟先把1、2运过去,然后再让1把船开回来,让98、99乘船过去,再让2把船开回来,这是一个循环。接下来让99和98在对岸不动,再把1、2运到对岸,1把船开回来,97、96乘船过去,2把船开回来。。。。就这样循环下去,最后注意奇偶的讨论就可以了。

可以自己试一下,两种方法解出来的时间是不一样的。但是在人数小于等于三时,第一种方法可以求出最优解,大于三时,第二种方法可以求出最优解。

#include<stdio.h>
int time[1010];
void sort(int a[],int n)
{
    int i,j,t;
    for(i=1;i<n;i++)
    {
        for(j=0;j<n-1;j++)
        {
            if(a[j]>a[j+1])
            {
                t=a[j];
                a[j]=a[j+1];
                a[j+1]=t;
            }
        }
    }
}

int main()
{
    int n,i,b,N;
        scanf("%d",&n);
        for(i=0;i<n;i++)
        {
            scanf("%d",&time[i]);
        }
        sort(time,n);
        int sum=0;
        while(n>3)
        {
            if(2*time[1]+time[0]>2*time[0]+time[n-2])
                sum+=2*time[0]+time[n-2]+time[n-1];
            else
                sum+=2*time[1]+time[0]+time[n-1];
                n-=2;
        }
        if(n==3)
        sum+=time[1]+time[0]+time[2];
        if(n==2)
        sum+=time[1];
        if(n==1) sum += time[0];
        printf("%d",sum);
    
    return 0;
}

1004 taozi的小难题

描述

从前有一个抠门的国王taozi,一天他的领土受到了其它国家的侵略,于是taozi决定组建一个骑士团击退敌人。当然招募骑士也是需要花费金币的,这里就以骑士的攻击力来给金币,抠门的taozi看到了这么多骑士前来,很头痛,于是taozi找到了聪明的你,让你去帮他解决这个问题。

输入

输入多组数据,对于每组数据,第一行输入一个n,m,分别代表n个敌人和m个骑士,接下来n个数代表敌人的攻击力a,然后m个数代表雇佣骑士的攻击力b,多组数据以n=0,m=0结束。
数据规定

1≤n,m≤10000

1≤a,b≤100

输出

对于每组数据,输出国王最少需要花费多少金币雇佣骑士才能杀死所有敌人,如果不行输出-1。

样例输入

2 3
5 4
7 8 4
2 1
5 5
10
0 0

样例输出

11
-1

提示

第一组数据,taozi选择第3号骑士打败2号敌人,选择第1号骑士打败1号敌人,总共花费11元。

第二组数据,taozi无论怎么选都打不过敌人。

题目规定了数组的范围时100,那就直接开两个100的数组就好,直接用下标进行比较。下标比较总比数组元素比较来的方便。这里的sum是金币的数值,a代表敌人的攻击力,b代表骑士的攻击力,那么把a放在外循环,把b放在内循环。让b数组从小到大遍历,这样可以做到和a最接近。这里的f是用来判断能不能找到,注意break只能中断离他最近的那个循环。

#include<stdio.h>
#include<string.h>
#define maxn 1001

int main()
{
    int i,j,k,n,m,x;
    int a[maxn],b[maxn];
    while(scanf("%d%d",&n,&m),n||m)
    {
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        for(i=0;i<n;i++)
        {
            scanf("%d",&x);
            a[x]++;
        }
        for(i=0;i<m;i++)
        {
            scanf("%d",&x);
            b[x]++;
        }
        int sum=0,f=1;
        for(i=1;i<=100;i++)
        {
            for(j=1;j<=a[i];j++)
            {
                f=0;
                for(k=i;k<=100;k++)
                {
                    if(b[k]>0)
                    {
                        b[k]--;
                        sum+=k;
                        f=1;
                        break;
                    }
                }
                if(f==0)break;
            }
            if(f==0)break;
        }
        if(f==0)printf("-1\n");
        else printf("%d\n",sum);
    }
    return 0;
}

未完待续

欢迎评论区,私信交流! 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

captainfly_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值