贪心算法(最优装载问题,田忌赛马,雷达问题)

3 加勒比海盗船——最优装载问题

在北美洲东南部,有一片神秘的海域,那里碧海 蓝天、阳光明媚,这正是传说中海盗最活跃的加勒比 海(Caribbean Sea)。17 世纪时,这里更是欧洲大陆 的商旅舰队到达美洲的必经之地,所以当时的海盗活 动非常猖獗,海盗不仅攻击过往商人,甚至攻击英国 皇家舰……
有一天,海盗们截获了一艘装满各种各样古董的 货船,每一件古董都价值连城,一旦打碎就失去了它 的价值。虽然海盗船足够大,但载重量为 C,每件古 董的重量为 wi,海盗们该如何把尽可能多数量的宝贝 装上海盗船呢?

输入格式:

第1行输入T组测试数据,每组测试数据输入载重量 c 及古董个数 n,下1行输入每个古董的重量wi,用空格分开.

输出格式:

每组能装入的古董最大数量

输入样例:

1
30 8
4 10 7 11 3 5 14 2

输出样例:

5

代码长度限制

16 KB

时间限制

400 ms

内存限制

64 MB

思路:

 这题没啥难度,他要求装的最大数量最多,所以就先排个序,然后从小的那头开始装就可以了。

代码:

#include<iostream>
#include<cstdio>
#include <algorithm>
using namespace std;
int HanShu(int c, int n, int wi[]);
int main()
{
	int T;
	cin >> T;
	for(int i = 0; i < T; i++)
    {
        int c; //载重量
        int n; //古董个数
        cin >> c >> n;
        int wi[n];  //每个古董的重量
        for(int j = 0; j < n; j++)
            cin >> wi[j];

        //先选轻的
        sort(wi, wi+n);
        cout << HanShu(c, n, wi) <<endl;
    }

	return 0;
}

//c装载量,n古董个数,wi每个古董重量
int HanShu(int c, int n, int wi[])
{
    int count = 0; //计算有几件古董
    int currentWeight = 0;  //计算当前重量
    for(int i = 0; i < n; i++)
    {
        //大于载重量直接停止
        if(currentWeight + wi[i] > c)
            break;
        //没大于载重量就往后加
        currentWeight += wi[i];
        count++;
    }
    return count;
}

4 田忌赛马

这是一个编程题模板。

这是中国历史上的一个著名故事。

大约 2300 年前,田忌是齐国的一位将军,他喜欢与国王等人赛马。

田忌和国王都有三匹不同等级的马----下马、中马、上马。

规则是一场比赛要进行三个回合,每匹马进行一回合的较量,单回合的获胜者可以从失败者那里得到 200 银元。

比赛的时候,国王总是用自己的上马对田忌的上马,中马对中马,下马对下马。

由于国王每个等级的马都比田忌的马强一些,所以比赛了几次,田忌都失败了,每次国王都能从田忌那里拿走 600 银元。

田忌对此深感不满,直到他遇到了著名的军事家孙膑,利用孙膑给他出的主意,田忌终于在与国王的赛马中取得了胜利,拿走了国王的 200 银元。

其实胜利的方法非常简单,他用下马对战国王的上马,上马对战国王的中马,中马对战国王的下马,这样虽然第一回合输了,但是后两回合都胜了,最终两胜一负,取得了比赛胜利。

如果田忌活在如今,那么他可能会嘲笑自己当初的愚蠢。

重要的是,如果他现在正参加 ACM 竞赛,他可能会发现赛马问题可以简单地看作是在二分图中找到最大匹配项。

在一侧画田忌的马,在另一侧画国王的马,只要田忌的一匹马能够击败国王的一匹马,我们就在这两匹马之间画一条边。

然后,赢尽可能多的回合,就变成了在这个二分图中找到最大匹配。

如果存在平局,问题会变得更加复杂,他需要为所有可能的边分配权重 0、1 或 −1,并找到最大加权的完美匹配…

然而,赛马问题其实是二分图匹配的一种非常特殊的情况。

该图由马的速度决定,速度快的总是能击败速度慢的。

这种情况下,加权二分图匹配算法就显得大材小用了。

在这个问题中,你需要编写一个程序,来解决这一特殊的匹配问题。

输入格式:

输入包含最多 50 组测试数据。

每组数据第一行包含一个整数 n(1≤n≤1000),表示一方马的数量。

第二行包含 n 个整数,是田忌的马的速度。

第三行包含 n 个整数,是国王的马的速度。

输入的最后一行为 0,表示输入结束。

输出格式:

每组数据输出一个占一行的整数,表示田忌最多可以获得多少银元。

输入样例:

在这里给出一组输入。例如:

3
92 83 71
95 87 74
2
20 20
20 20
2
20 19
22 18
0

输出样例:

在这里给出相应的输出。例如:

200
0
0

代码长度限制

16 KB

时间限制

400 ms

内存限制

64 MB

思路:

        根据那个故事所说,本题的思路就是,先看自己最快的马和对面最快的马谁快,自己快的话就 赢了对面,对面快的话就用自己跑的最慢的马跟对面跑的最快的马比。

        如果平手的话,就看看自己最慢的马和对手最慢的马哪个快,自己快的话,就用最慢的马跟对手最慢的马比,对面快的话,就用自己最慢的马跟对面最快的马比(就是跟自己最快的马一样快的那匹)。

代码:

#include<iostream>
#include<cstdio>
#include <algorithm>
using namespace std;
int HanShu(int tiJi[], int king[], int n);
int cmp(int a, int b);

int main()
{
	int n;

	while(cin >> n && n != 0)
    {
        int tiJi[n];
        int king[n];
        for(int i = 0; i < n; i++)
        {
            cin >> tiJi[i];
        }
        for(int i = 0; i < n; i++)
        {
            cin >> king[i];
        }
        //从大到小排列
        sort(tiJi, tiJi+n,cmp);
        sort(king, king+n,cmp);

        cout << HanShu(tiJi, king, n) <<endl;

    }

	return 0;
}

//只需要从大到小跟对面从大到小的比,能比过立刻换下一个比
int HanShu(int tiJi[], int king[], int n)
{
    int lefta = 0, righta = n-1;  //田忌的马的编号
    int leftb = 0, rightb = n-1;  //齐王的马的编号
    int win = 0; //能赢几次
    int fail = 0; //输几次
    while(lefta <= righta)
    {
        //田最快的马比王最快的马快,就直接比
        if(tiJi[lefta] > king[leftb])
        {
            win++;
            lefta++; leftb++;
        }
        //慢,就用跑的最慢的马跟王最快的马比
        else if(tiJi[lefta] < king[leftb])
        {
            fail++;
            righta--; leftb++;
        }
        //平局就比较俩人最慢的马谁慢
        else
        {
            //田最慢的马比王最慢的马跑得快,就比较最慢的
            if(tiJi[righta] > king[rightb])
            {
                righta--; rightb--;
                win++;
            }
            //否则用田最慢的马去比王最快的马
            else
            {
                if(tiJi[righta] < king[leftb])
                    fail++;
                righta--; leftb++;

            }

        }
    }
    return (win-fail)*200;
}

int cmp(int a, int b)
{
    return a > b;
}

5 雷达问题

假设海岸是一条无限直线。陆地在海岸的一侧,大海在另一侧。每个小岛都是位于海边的一个点。而任何位于海岸的雷达装置只能覆盖d距离,因此,如果它们之间的距离最多为d,则海中的岛屿可以被半径装置覆盖。

我们使用笛卡尔坐标系,定义海岸是x轴。海面在x轴上方,陆地一侧在x轴以上。给定海中每个岛屿的位置,并给定雷达安装的覆盖范围的距离,您的任务是编写一个程序来查找覆盖所有岛屿的最小数量的雷达安装。请注意,岛屿的位置由其 x-y 坐标表示。

输入格式:

输入由多个测试用例组成。每种情况的第一行包含两个整数 n (1<=n<=1000) 和 d,其中 n 是海中岛屿的数量,d 是雷达装置的覆盖距离。后跟 n 行,每行包含两个整数,表示每个岛位置的坐标。然后跟着一个空行来分隔样例。
输入由一条包含零对的数据终止。

输出格式:

对于每个测试用例输出一行,包括测试用例编号,后跟所需的最小雷达安装数量。“-1”安装意味着没有针对这种情况的解决方案。

输入样例:

在这里给出一组输入。例如:

3 2
1 2
-3 1
2 1

1 2
0 2

0 0

输出样例:

在这里给出相应的输出。例如:

Case 1: 2
Case 2: 1

代码长度限制

16 KB

时间限制

400 ms

内存限制

64 MB

思路:

        根据题意,雷达都在x轴上,信号范围为d,要求出几个雷达能覆盖全部的岛屿。

        所以其实我们就可以将问题转化,以岛屿的坐标为原点,半径为d的圆之内,就是雷达能放的位置范围。又因为雷达只能放在x轴上(海岸上),所以就是每个岛屿的圆边与x轴的两个交点的范围,是雷达能放的范围(如果刚好离x轴距离为d,那么就是一个交点。如果超过了d,那说明没法覆盖,输出-1)。

        然后就从x轴上的范围的左侧或右侧开始判断,如果前一个岛屿在x轴上的范围与下一个岛屿在x轴上的范围相交了,说明可以共用一个雷达,就判断下一个岛屿。如果下一个岛屿不相交了,说明要多用一个雷达,然后我们重新在不相交的这个岛屿和它的下一个岛屿进行判断,以此类推到结束即可。

 代码:

#include<iostream>
#include<cstdio>
#include <algorithm>
#include <math.h>
using namespace std;

typedef struct Range  //每个岛屿与x轴的左右交点
{
    double left;
    double right;
} Range;
Range range[1010];
int cmp(Range a, Range b);

int main()
{
	int n; //岛屿数量
	int d; //雷达覆盖距离
	int count = 1; //第几组数据
	bool isout = false;

	while(cin >> n >> d && n != 0 && d != 0)
    {
        int island[n][2]; //n个岛的坐标
        for(int i = 0; i < n; i++)
        {
            cin >> island[i][0] >> island[i][1];
            if(island[i][1] > d)//超过雷达覆盖范围,-1
            {
                //
                isout = true;
                break;
            }
        }
        //超出了
        if(isout == true)
        {
            cout << "Case " << count << ": " << -1 << endl;
            isout = false;//初始化下一次用
        }
        //计算每个岛屿与x轴的左右交点
        else
        {
            for(int i = 0; i < n; i++)
            {
                //用勾股定理
                double num = sqrt(pow(d,2) -pow(island[i][1],2));
                range[i].left = island[i][0] - num;
                range[i].right = island[i][0] + num;

            }

            //对得到的范围,用左侧的大小排个序
            sort(range,range+n, cmp);

            //for(int i = 0; i < n; i++)
                //cout << range[i].left << ' '<< range[i].right << endl;
            //cout << "----------------"<<endl;


            //判断下一个的left,在不在当前范围的right内
            //在的话可以公用,不在就得多加一个
            int radar = 1;
            int i = 0, j = 1; //i是左侧岛屿范围编号,j是下一个岛屿范围编号
            while(j < n)
            {
                if(range[i].right >= range[j].left)
                {
                    j++; //这个岛可以公用一个雷达,判断下一个岛
                }
                else
                {
                    radar++; //这个岛超过了范围,要多用一个雷达,比较的两个岛屿都换一下
                    i = j;
                    j++;
                }

            }
            cout << "Case " << count << ": " << radar << endl;
        }
        count++;
    }


	return 0;
}



int cmp(Range a, Range b) // 按left从小到大排序
{
    return a.left < b.left;
}

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

百年bd

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

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

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

打赏作者

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

抵扣说明:

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

余额充值