一、题目大意
小伟报名参加中央电视台的智力大冲浪节目,本次挑战赛吸引了众多参赛者,主持人为了表彰大家的勇气,先奖励每个参赛者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;}//表明未找到空闲时间段完成本游戏,继续循环找elsecontinue;}}}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;
}