【题目描述】
小伟报名参加中央电视台的智力大冲浪节目。本次挑战赛吸引了众多参赛者,主持人为了表彰大家的勇气,先奖励每个参赛者 m 元。先不要太高兴!因为这些钱还不一定都是你的?!接下来主持人宣布了比赛规则:
首先,比赛时间分为 n个时段,它又给出了很多小游戏,每个小游戏都必须在规定期限 ti前完成。如果一个游戏没能在规定期限前完成,则要从奖励费 m元中扣去一部分钱 wi,wi为自然数,不同的游戏扣去的钱是不一样的。当然,每个游戏本身都很简单,保证每个参赛者都能在一个时段内完成,而且都必须从整时段开始。主持人只是想考考每个参赛者如何安排组织自己做游戏的顺序。作为参赛者,小伟很想赢得冠军,当然更想赢取最多的钱!注意:比赛绝对不会让参赛者赔钱!
【输入格式】
输入共四行。
第一行为 m,表示一开始奖励给每位参赛者的钱;
第二行为 n,表示有 n 个小游戏;
第三行有 n 个数,分别表示游戏 1 到 n 的规定完成期限;
第四行有 n个数,分别表示游戏 1到 n不能在规定期限前完成的扣款数。
【输出格式】
输出仅一行,表示小伟能赢取最多的钱。
【样例输入】
10000
7
4 2 4 3 1 4 6
70 60 50 40 30 20 10
【样例输出】
9950
【数据范围与提示】
对于 100% 的数据,有 n≤500,1≤ti≤n。
思路:说实在的,我刚开始真的被这个题目给搞蒙了,因为题目没有说清楚,但是其实要是理清楚了题目的话这道题真的很简单很简单,其实就是一个稍微复杂一点的判断这个数用没用过。我先来分析一下题目:有n个游戏,每个游戏都有限定的时间和没有完成的时候的相应的处罚,求最小的处罚(这个就是题目最最最简单的分析。)然后,这样之后,我们第一个想到的是要编目录(也就是结构体),因为我们要求最小的,既然是最小最大这一类的,自然要排序,这样才能使我们在筛选的时候更加的方便。可能有一个不容易想到,那就是在排序的时候,我们要按照处罚从大到小排序,为什么要这样排序呢?因为我们从大到小的话,就会使我们在前面的判断的时候可以直接把最大的处罚先去掉,然后到后面不可行的时候,就是最小的处罚,这样才可以使我们所得到的处罚最小。大致的思路理解了吗?噢噢噢噢还有一个重点,就是我们在判断当前的这个时间期限可不可以用的时候,一定不可以鲁莽的判断表层的时间,而是要深入判断从当前的这个表层时间到时间为1的时候,只有全部都不可以,才是这的不可以。
【代码实现,注释写的很详细,快读不解释我发过博客的】
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read()//日常快读
{
char c=getchar();
int x=0,f=1;
while(c<48 || c>57)
{
if(c=='-') f=-1;
c=getchar();
}
while(c>=48 && c<=57)
{
x=x*10+c-48;
c=getchar();
}
return x*f;
}
struct node
{
int t,w;//t表示时间期限 //w表示罚款
}a[110000];
bool cmp(node n1,node n2)
{
return n1.w>n2.w;//按罚款额从大到小排序
}
bool bk=false;//这个是判断要不要罚款,一开始初始化全部都要罚款
bool v[110000];//这个是用来判断当前这个时间期限的所有期限要不要罚款
int main()
{
memset(v,true,sizeof(v));//初始化全部都不用罚款
int m,n; m=read(); n=read();
for(int i=1;i<=n;i++) a[i].t=read();
for(int i=1;i<=n;i++) a[i].w=read();
sort(a+1,a+n+1,cmp);//按罚款额的大小进行排序
for(int i=1;i<=n;i++)
{
bk=false;//最开始要罚款
for(int j=a[i].t;j>=1;j--)
/*
这个是按时间期限来枚举判断
因为我们要按时间的完成度来罚款
所以自然就是以时间来罚款的啦
还有一个就是如果我要知道这一种情况到底是不是不可以的
那我就一定要把这个时间的每一个时间点都给计算一遍
如果每一次都不可以的话,说明这个任务就是不能完成的
*/
{
if(v[j]==true)
/*
如果这一步进入不了的话
那就继续上面的j的循环,而不是退出到i的循环
这样循环是为了对当前的这一个游戏公平
说当前的游戏不能做的话,
一定是因为他的所有期限都完成不了,才可以罚款
所以当前的这一个不行不代表前面的不行
比如说:期限为4的话,4我们记录过不行,
但是3,2,1我们没有记录那就不能说我们当前的这种情况要完成不了,
就不能说当前的这一个游戏我们要罚款
*/
{
v[j]=false;
/*
就把当前这种方案变为false,表示记录过
下一次在出现的话就要判断为false,也就是不能用
*/
bk=true;//然后bk=true,说明不用罚款
break;//退出循环,到下面判断到底需不需要罚款
}
}
if(bk==false) m-=a[i].w;
/*
注意这一步不是最后判断的,而是在上面的break之后
就立刻判断,如果经过了上面那一步不成立的话
就说明当前的这一种方式不可以走,所以就要罚款
*/
}
printf("%d\n",m);//剩下的钱就是可以拿到的最多的钱
return 0;
}
/*
样例输入
10000
7
4 2 4 3 1 4 6
70 60 50 40 30 20 10
样例输出
9950
样例解释:第5个游戏和第6个游戏不能完成
到第五个游戏的时候,1到4这四个期限在v数组里面都已经是false
所以第五个的1就直接false了,就要罚款30元
然后到第六个游戏的时候,1到4这四个期限还是不行的
所以就直接false掉了,就要罚款20元
第七个游戏就可以,因为期限为6在前面没有出现过
所以就是10000-30-20=9950
*/
代码就是这样,要是有大佬发现什么错误,或者有什么大佬感觉怪怪的,望指出。