#10004. 「一本通 1.1 例 5」智力大冲浪

【题目描述】

小伟报名参加中央电视台的智力大冲浪节目。本次挑战赛吸引了众多参赛者,主持人为了表彰大家的勇气,先奖励每个参赛者 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 
*/ 

代码就是这样,要是有大佬发现什么错误,或者有什么大佬感觉怪怪的,望指出。 

 

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值