貌似是签到题?反正是日常昏睡比赛 打完吃饭的时候和某dd+大佬讨论(其实是单方面被授课)回来后又自己爆交几次才推出来了。
先贴个题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6651
题目大意:Cuber QQ 考前复习,已知考试总共有n道题,总分值是n分,每道题的分值未知,然后这位同学想保证自己能写出其中的k道题,而为了写出分值为a的某道题该同学要花a+1的时间复习这个知识点,另外,每道题知识点各不相同。
思路:最先想到的可能是均分,但是均分不行,很显然的不行,因为均分样例都过不了。
于是开始反思,要怎样才能过样例(对,先把样例过了再说)。
最懒的方法,特判一下,我们可以看到两个样例都是n=k的情况,那么显然为了保证全部写出,我们要复习(m+1)*k的时间。
但这貌似并不能具有特殊性,当看k<n时能不能这么做呢?
可以尝试一下,比如改变第二个样例的k值,把它改成9,那么假如我们按照先前的做法,我们得到的答案依然为1100,列出十道题复习所花的时间如下:
110 110 110 110 110 110 110 110 110 110
然后开始思考,显然这个答案是可以保证该同学能写对9道题,但是这个答案未免太大了?尝试减少其中的某个值,再来看看行不行。比如这样:
55 110 110 110 110 110 110 110 110 110
十分豪气地把第一个值砍掉了一半,但我们可以看出,即使砍掉了那么多,这个同学按照这样的时间进行复习还是可以轻松写出9道题,因为总分值m小于上面那个序列中任意两值的和。我们可以假设出题人和这位同学过不去,有意为难他并且想办法知道了他的复习策略,那么为了让他无法写出9道题,出题人该怎么分配自己手里的m分?首先我们要明确一点,假如该同学复习某道题花的时间是a,而这道题分值是b,当a<=b时,他都无法写出这道题。
那么为了让该同学无法写出9道题,出题人只要保证这位同学 有两道题写不出来/只能写出8道题 就行了,也就是说,他可以先这样赋分。
复习方案:55 110 110 110 110 110 110 110 110 110
赋分方案:X X 0 0 0 0 0 0 0 0
x表示还没有赋分,也就是说,出题人先送了该同学八道题,从而保证了自己手里有足够的可以利用的分值来阻止他写对剩下的两道题。那他首先保证该同学有一道题是写不出来了。
复习方案:55 110 110 110 110 110 110 110 110 110
赋分方案:55 X 0 0 0 0 0 0 0 0
现在这位同学第一题没有写出来,出题人手里,很不幸,只剩54分了,而该同学第二题准备了足足110的复习时间,那么出题人是无法阻止他写对9道题了。
由此我们也可以得到,第二题其实这个同学只要花55的时间就够了。那么在思考一下,其他题目真的有必要话110的时间么?
显然不用,我们可以思考一下其他题目的复习时间有什么限制条件(使出题人总是选择放过这些题)。
显然,为了利益最大化,出题人总是会在他觉得选择不放过这些题目会导致他亏的情况下放过这些题目,那么出题人使这些题目写不出来的代价应当比第一第二题大,或者至少与第一第二题中花的代价较多的那题相等,也就是说这些题目的复习时间就应该>=max(t1,t2)。(田忌赛马)
为了尽可能节省复习时间,我们显然取等于的情况,那么就得到了这个样例的最佳复习方案:
最佳复习方案:55 55 55 55 55 55 55 55 55 55
这只是一种特殊的情况,得到的是一个特殊的结论,现在要从特殊推导到一般。
为什么出题人只打算在前两道题为难该同学?因为出题人的目的是阻止该同学写出9道题,也就是说写出k道题,那么该同学写出(k-1)道题对出题人来说完全没有问题甚至可以送他(k-1)道题,也就是对于其中的(k-1)道题,出题人可以使之分值为0(这也是出题人的最佳策略,以便留下足够的分值来阻止该同学写出剩下的题目)。我们设一个变量cnt=n-k+1,那么出题人只需要使该同学有cnt道题写不出来,他的目的就达到了,此时,出题人手里有m分,要分给cnt道题。
接下来是该同学的时间,首先,该同学并不知道出题人会在哪几道题上赋0(正如一般博弈的前提,双方都足够聪明可以推断出所以能推断的条件,那么该同学也能想到出题人的最佳策略),那么他就应当有一个基准值,每道题花的复习时间都大于等于这个基准值,关键是这个基准值是多少。如果此时想起还有一个cnt在,那就ok了。我们要到cnt里去求那个基准值。我们知道出题人会在cnt道题里分配m分,但我们并不知道他会怎么分配,但总之为了在cnt道题里至少对一道,我们要在这cnt道题里花(m+1)的时间(因为“假如该同学复习某道题花的时间是a,而这道题分值是b,当a<=b时,他都无法写出这道题”)。接下来就是均分了,也就是每道题至少花(m+1)/cnt的时间,这个值也应当是我们所需要的基准值,但是这个式子存在一个问题:(m+1)不一定被cnt整除,余数怎么办?显然不能省略掉,那就要加回去,加回去的同时也要尽可能均分,那么假设(m+1)/cnt=k.......h,我们要在cnt道题中选取其中的h道分别加上1复习时间,这样就保证了这cnt道题肯定有一道能写出来(验证方法同上),此时我们不能忘了前面还有个结论是“剩余题目的复习时间>=max(t1,t2)”,max(t1,t2)是当cnt=2时的说法,那么转换成一般的表述就是剩余(k-1)道题的复习时间应当>=cnt道题中的最大复习时间,也就是 (m+1)/cnt+(m+1)%cnt==0?0:1
贴代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<set> 4 #include<stdio.h> 5 #include<string.h> 6 #include<math.h> 7 #include<vector> 8 #include<stdlib.h> 9 #include<queue> 10 #include<algorithm> 11 #include<map> 12 #include<stack> 13 using namespace std; 14 int main() 15 { 16 int t; 17 scanf("%d",&t); 18 while(t--) 19 { 20 long long n,m,k; 21 scanf("%lld%lld%lld",&n,&m,&k); 22 long long part=0; 23 long long cnt=n-k+1; 24 part=(m+1)/cnt; 25 if((m+1)%cnt!=0) 26 { 27 part++; 28 cnt-=(m+1)%cnt;//part+1后有部分题目其实并不需要+1,此处的cnt就是这些题目的个数,以便在输出时直接减去 29 } 30 else 31 { 32 cnt=0;//假如(m+1)被整除,没有必要减去,就把cnt变成0,就不用改输出的式子了(偷懒) 33 } 34 printf("%lld ",n*part-cnt); 35 } 36 return 0; 37 }
欢迎交流讨论。