C语言(CED)智力大冲浪——贪心算法第一题

一、题目大意

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

输入:

输入文共4行。

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

第2行为n,表示有n个小游戏;

第3行有n个数,分别表示游戏1到n的规定完成期限;

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

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

【sample input】

  10000

  7

  4   2    4   3   1   4   6

  70  60   50  40  30  20  10

【sample output】

  9950

二、大致思路

        这道题数据有很多,但是要学会排除掉多余干扰因素。因为要让剩余的奖金最大化,所以我们可以按照未完成游戏时所扣金额的大小排序(这里我按照从大到小进行排序),首先选择进行扣钱金额大的游戏。那么问题来了,每个游戏都有固定的截止时间,有很多扣钱大的游戏相互冲突,怎么办?相信很多人都会卡在这个问题上。我们可以通过以下方法实现所得奖金最大化:我们按上述方法排序后,从左到右遍历,能在对应时间段完成的,就完成;不能在该时间段完成的,就找一个在该游戏截止时间之前的时间段完成游戏,如果找不到,则放弃。

        下面以一个具体的例子进行遍历:

        游戏截止时间: 4    2     4    3    1    4    6

        游行名称:        A    B    C    D   E    F   G

         所扣金额:      70  60   50  40  30  20  10

   为了方便起见,我在这里就统一用时刻代表何时完成游戏(时刻和时间段不一样)。如上表所示,从左到右对游戏进行遍历,我们在4s进行A游戏2S进行B游戏,下一个游戏本来要在4s进行,但是4S已经被占用了,所以要向下(游戏截止前)找一个空余时间去完成该游戏,在对时间向下遍历的时候发现3S没被占用,所以用3S进行C游戏继续遍历,遇到D游戏,但是3S已经被占用,同理对时间向下遍历:2S不行,1S可以,所以用1S进行D游戏继续对游戏向右遍历,遇到E游戏,本来在1S进行,但是被占用了,然后对时间向下遍历,在1S前没有空时间,所以就放弃E游戏。对游戏继续遍历,同理F游戏也得放弃,最后的G游戏可以进行。整个遍历的过程就是这样,复杂度N方。

三、具体实现

        我用C语言写的代码,在代码开头,定义一个数组,并初始化为0,用来对各个时间是否被使用进行标记,1为使用。然后定义一个game结构体,并设置f(finish代表游戏截止时间),d(decrease代表罚金),p(position代表有一是否进行1表示进行,0代表未进行)三个属性。之后就是照着之前的分析进行代码设计。

#include<stdlib.h>
#include<algorithm>
#include<stdio.h>
#include<iostream>
using namespace std;
int  m;//代表开始奖励给每位参赛者的钱
int  b[6000] = { 0 };//记录时间段是否被使用,使用标记为1
//定义一个游戏结构体,存储游戏的属性
struct game
{
	int f;//表示游戏1到n的规定完成期限
	int d;//表示游戏1到n不能在规定期限前完成的扣款数
	int p = 0;//1表示对应的游戏已完成,0表示未完成
};
//用bool函数对排序依据的因素进行选择
bool cmp(game f, game d)
{
	return f.d > d.d;//选择罚款项进行非增序排序
}
//此函数为计算最小扣款函数
int compu(int n, game a[])
{
	sort(a, a + n, cmp);//对罚款进行非递增排序
	int i, j;//循环变量
	//外层循环对已排好序的游戏,进行从左到右的遍历
	for (i = 0; i<n; i++)
	{
		//如果游戏对应的时间段没有被占用,则进行该游戏
		if (b[a[i].f] == 0)
		{
			a[i].p = 1;//对游戏中是否完成的属性进行修改,表明该游戏已完成
			b[a[i].f] = 1;//对游戏使用的时间段进行标记
			continue;//进行下一个游戏的判断
		}
		//游戏对应的时间段已被占用
		else
		{
			for (j = (a[i].f - 1); j >0; j--)
			{
				//表明找到空闲时间段完成本游戏
				if (b[j] == 0)
				{
					a[i].p = 1;//对游戏中是否完成的属性进行修改,表明该游戏已完成
					b[j] = 1;//对游戏使用的时间段进行标记
					break;
				}
				//表明未找到空闲时间段完成本游戏,继续循环找
				else
					continue;
			}
		}
	}
	return m;
}
int main()
{
	game a[6000];//定义游戏
	int n;//表示有n个小游戏
	cout << "请输入开始时奖励给每位参赛者的钱款金额:" << endl;
	cin >> m;
	cout << "请输入小游戏的个数:" << endl;
	cin >> n;
	int i = 0;//循环变量
	cout << "请输入每个游戏规定完成的期限:" << endl;
	for (i = 0; i <n; i++)
	{
		cin >> a[i].f;
	}
	cout << "请输入每个游戏不能在规定期限前完成的扣款数:" << endl;
	for (i = 0; i < n; i++)
	{
		cin >> a[i].d;
	}
	int sum = 0;//表示小伟的获奖金额
	sum = compu(n, a);
	for (i = 0; i < n; i++)
	{
		if (a[i].p == 0)
			sum = sum - a[i].d;
	}
	if (sum < 0)
		sum = 0;
	cout << "小伟最多得奖的金额数为:" << sum << endl;
	system("pause");
	return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

liO_Oil

打赏我,开启隐藏模式。

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

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

打赏作者

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

抵扣说明:

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

余额充值