题意:
对于每一个月来说,如果盈利则盈利S,如果亏空则亏d。每五个月进行一次统计,共统计八次(1-5月一次,2-6月一次.......)
统计的结果是这八次都是亏空。问题:判断全年是否能盈利,如果能则求出最大的盈利。如果不能盈利则输出Deficit
MS公司每月要么盈余,要么亏损,且一年中每个月的盈余是一样的,亏损也是一样的。
财务统计是每五个月统计一次收入总额,即1-5,2-6……8-12,共有8次统计结果,并且这八次结果公司都亏损的。
给出surplus和deficit,求出全年最大的盈余数。
*/
#include <stdio.h>
int getMaxSurplus(int surplus, int deficit) {
if (surplus >= 4*deficit) {
return -1;
}
int maxSurplusMonthNumIn5Months = 5*deficit/(surplus + deficit);
// if ( (maxSurplusMonthNumIn5Months*surplus - (5 - maxSurplusMonthNumIn5Months) * deficit) >= 0) {
// return -1;
// }
deficit = -deficit;
int monthStatus[12];
int surplusSum = 0;
int amountIn5Moths = 0;
int FiveMonthBegin = 0;
for (int i = 0; i < 5; i++) {
if (maxSurplusMonthNumIn5Months > 0) {
maxSurplusMonthNumIn5Months--;
monthStatus[i] = surplus;
surplusSum += surplus;
amountIn5Moths += surplus;
} else {
monthStatus[i] = deficit;
surplusSum += deficit;
amountIn5Moths += deficit;
}
}
for (int i = 5; i < 12; i++) {
amountIn5Moths -= monthStatus[FiveMonthBegin];
FiveMonthBegin++;
if (amountIn5Moths + surplus < 0) {
monthStatus[i] = surplus;
surplusSum += surplus;
amountIn5Moths += surplus;
} else {
monthStatus[i] = deficit;
surplusSum += deficit;
amountIn5Moths += deficit;
}
}
// printf("=====================\n");
// for (int i = 0; i <12; i++) {
// printf("%d ", monthStatus[i]);
// }
// printf("\n=====================\n");
if (surplusSum < 0) {
return -1;
} else {
return surplusSum;
}
}
int main() {
int surplus, deficit;
while(scanf("%d %d", &surplus, &deficit) != EOF) {
int annualSurplus = getMaxSurplus(surplus, deficit);
if (annualSurplus == -1) {
printf("Deficit\n");
} else {
printf("%d\n", annualSurplus);
}
}
}
题写的很晦涩....
本题因为枚举空间小,所以即使用最笨的穷举法, 都能AC。
但是最好的做法还是贪心法, 只不过证明贪心选择是OK的需要想想:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
根据经验,贪心选择往往都在极端处选择。我们看下1-5月里,亏损的月数肯定应尽量往后选,证明如下(虽然很明显,但证明有利于使思维更清晰)
假设输入s=7d=5,那么前5个月至少亏损3个月。我们先证明3,4,5月必定被选中。优解中第5个月不选,那随便把前面某个亏损月和第五个月交换,不破换连续5月亏损的条件,所以得到另一个最优解。同理再选第四个,第三个。我们再证明只有3,4,5月被选中,否则得不到最优。因为假设2月被选中。2月包含在1-5,2-6中,对这两组而言,2必定已经是多余的了(1-5 2-6 中已经有了345可以保证区间<0, 而标记2对后面的区间没有任何影响,在345被标记的前提下),因为包含3,4,5就可以使条件满足;而对3-6,4-7...而言,2是否选取对他们没有影响。因此如果最优解包含2,那么去掉2必定是个更优解。得证。所以选3,4,5必定是个贪心选择。
处理完1-5后,剩下的问题就好办了呵呵。
其实这题的子问题结构倒不是很清晰了,可以认为分叉出现在1-5月的选择中,接下来都是一条直线了(这个问题比较独特的一点,因为各种约束条件, 导致其实贪心的选择空间在1~5就已经定下了全局)(1-5月选定后,6月是s还是d由2-6必亏损这一条件已经确定了,以此类推)。假设最少有3个月亏损,那么1-5月的选择共有c(5,3)+c(5,4)+c(5,5)种情况,所以是一个16叉树,除了根节点外其他节点不再分叉。贪心选择选取其中一根树枝,因为之后不分叉,所以总共只做了一次贪心选择。
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
还有做法更进一步,利用上面的准则,直接推导出了总共的5种可能情况,然后根据 s 和 d来进行选择:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
1、若SSSSD亏空,那么全年可能最大盈利情况为: SSSSDSSSSDSS
2、若SSSDD亏空,那么全年可能最大盈利情况为:SSSDDSSSDDSS
3、若SSDDD亏空,那么全年可能最大盈利情况为: SSDDDSSDDDSS
4、若SDDDD亏空,那么全年可能最大盈利情况为: SDDDDSDDDDSD
5、若DDDDD亏空,那么全年可能最大盈利情况为: DDDDDDDDDDDD
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
这种每个子问题不是相互独立的问题, 第一感觉就应该是动态规划, 不过一般来说, 想出了动态规划, 那么就应该尝试一下贪心, 如果成功, 自底而上 和 自上而底的效率是不一样的。
贪心法真是靠直觉和经验的, 连算法导论上都承认, 贪心每一个固定的模式。
最后还要在scanf上加上 !=EOF, 否则 output limit exceeds.
一行debug的log没消, WA了好几次。
发现有时候基础的数学运算都生疏了.