题目

本题的贪心算法策略需要深入思考一下

看到题目,最初没有理解题目的要求:看尽量多的完整的节目。尽量多是指数量多,自己理解成观看的时间最长。这样想其实简化了这道题。

正确理解题意后,首先想到的想法是:选择一个节目A,结束后选择另一个节目,如果节目A的时间同时覆盖了节目BC的时间,那么A就不应该看。怎么选择合适的节目?如果都把所有的节目考察一遍,统计看到的节目数量,成了穷举法,不是贪心算法。

遇到第一个关卡:如何在思维上找到突破口/切入点,当思路到达“如何选择合适的节目”这个问题时卡住,应该进一步思考“什么是合适的节目?”

这道题的贪心策略不再显而易见,像是解数学题,某个思路可以吗?需要深入思考、验证,不能蜻蜓点水之后就草率感觉“这样的思路好像不行吧”。

一个判断逻辑没有选择好浪费了大段时间

问题是这样的:

当遇到“当前节目能不能计入总数?”这个问题时,思路就绕了一小会,产生了下面的邪恶的代码。

这段代码在本地测试样例输入后,通过,到OJ上答案错误。

开始分析“样例中没有涉及但可能出现的测试数据”:如果节目A和节目B的时间完全重叠呢?详情见代码中的【错误1】

根据这个新情况修改一次后,造成【错误2】

修改错误2,造成【错误3】

感受:太着急的想法引来的问题多,复杂的想法引来的问题多。很多“直接的办法”往往是简单且可行的。

当一遇到“这个办法怎么这么复杂,要考虑的情况怎么这么多”的感觉时,你就要提高警惕了:思路上很有可能已经误入歧途。

很多时候,复杂不是问题本身困难,而是解决方法有待改进的征兆。

而误入歧途的根源是:着急,懒惰。想到一个办法就立马采用,而不进一步斟酌。

罪魁祸首:

for (former = i - 1; former > 0; former--)
{
if( list[i].tag == 1 && list[former].T_e > list[i].T_s)
{
break;
}
if(former == 0)
{
num++;
list[i].tag = 1;
}
}
/*former是游标,依次指向前面的节目
*num是可以收看的节目的总数,记录的是整个程序的最终输出结果
*tag是标记某个节目是否收看过了
*/

这段代码的背后的想法是:当前节目current能否计入num?取决于current的开始时间是否在前面“收看过的节目”的时间段中。于是拿着current的开始时间去和前面标记为已收看(tag==1;)的节目的时间段比较。

去和前面的时间比较!!!

一个罪恶的想法:去和前面的数据比较

但凡是要回头在前面处理过的数据中再捯饬,都是需要谨慎谨慎再谨慎的!

就目前遇到的程序中,似乎需要回头的时候,总有一个利器可以代替回头看:用一个变量/哨兵记录一下“目前处于什么状态”

太有用了!太好用了!

答案中的做法:

int currentTime = 0;
for (int i = 0; i < n; i++)
{
if (currentTime <= list[i].T_s)
{
currentTime = list[i].T_e;
num++;
}
}

无需回头看,无需tag标记。

写到这里,仔细想想,最初都想到了用tag标记是否看过某个节目,这就是向已经处理过的数据中添加tag的想法。为什么不更直接点,将“当前时间”作为tag来表示数据处理到了何处?!

功力不够,继续修炼!

完整代码

#include <stdio.h>
#include <algorithm>
using namespace std;
struct show {
int T_s;
int T_e;
int tag; //tag==0表示未看,tag==1表示已看 //用于排序sort
bool operator < (const show & B) const
{
return T_e < B.T_e;
}
} list[105];
int totalNum[105], output = 0;
int main()
{
int n;
//int num = 0;
int num;
while (scanf("%d", &n) != EOF)
{
if (n == 0)
break;
num = 0; //清除上一个测试用例的计数结果
for (int i = 0; i < n; i++)
{
scanf("%d%d", &list[i].T_s, &list[i].T_e);
list[i].tag = 0;
}
sort(list, list + n);
//list[i]为当前节目——当前结束最早的节目 for (int i = 0; i < n; i++)
{
int former;
if (list[i].tag == 0)
{
//错误2的版本:for(former = i; former > 0; former--)
for (former = i - 1; former > 0; former--)
{
//如果前面没有冲突则num++
//如果前面有冲突则i++
/*【错误1】
/*if中添加了tag的检查,这个造成了错误:如果list[0]和list[1]完全一样则多计数一次
*因为当考察list[1]时,list[1].tag==0,无法在former==1时break
*当former==0时,该if满足,但是进入if后break与否都不影响former==0
*break:说明时间冲突,但是此时former仍然==0,导致有冲突的情况下节目数仍要加1
*不break:说明时间没有冲突,此时是正确的。
*if (list[former].tag == 1 && list[former].T_e > list[i].T_s)
*尝试改进:去掉list[former].tag==1这个条件,造成错误2
*/
/*【错误2】这个if条件会导致former==i时,即节目A和节目A比较,而节目A.T_e>节目A.T_s恒成立
*导致后面所有的输入都不被正确处理
*改进:former的初始值从i-1开始, 导致错误3
*/
/*【错误3】回到了原点,下面这个if中没有list[former].tag == 1的检查,会造成节目A并没有收看,
*但考察节目B(i=B)时,节目B的时间与A去比较,由于A压根没有看,所以不需要与A比较!
*另外一个错误:当i==0时,former==-1,导致list[0]这个第一个节目永远都无法标记为可以观看的。
*问题的根源:if(former == 0) num++,这个增加可收看的节目个数的“判断条件”选的非常不好!!!
*/
if(list[former].T_e > list[i].T_s)
{
break;
}
}
if (former == 0)
{
num++;
list[i].tag = 1;
}
}
}
totalNum[output++] = num; /*这是答案中的版本,简洁明了!!!!!!!!!!!!!!!!!!
*思想是:添加一个指向当前时间点的哨兵currentTime
*于是,不再需要回顾(关注)前面节目的时间段了!
*直接将currentTime与待考察的节目队列中下一个节目的开始时间T_s比较
*currentTime<=list[i].T_s说明当前时间在下一个节目开始之前,则下一个节目可以收看!
int currentTime = 0;
for (int i = 0; i < n; i++)
{
if (currentTime <= list[i].T_s)
{
currentTime = list[i].T_e;
num++;
}
}
totalNum[output++] = num;
*/
} for (int i = 0; i < output; i++)
{
printf("%d\n", totalNum[i]);
}
return 0;
}

【九度OJ】题目1434贪心算法的更多相关文章

  1. 九度OJ 1433 FatMouse -- 贪心算法

    题目地址:http://ac.jobdu.com/problem.php?pid=1433 题目描述: FatMouse prepared M pounds of cat food, ready to ...

  2. 九度OJ 1082 代理服务器 -- 贪心算法

    题目地址:http://ac.jobdu.com/problem.php?pid=1082 题目描述: 使用代理服务器能够在一定程度上隐藏客户端信息,从而保护用户在互联网上的隐私.我们知道n个代理服务 ...

  3. 九度OJ 题目1384:二维数组中的查找

    /********************************* * 日期:2013-10-11 * 作者:SJF0115 * 题号: 九度OJ 题目1384:二维数组中的查找 * 来源:http ...

  4. hdu 1284 关于钱币兑换的一系列问题 九度oj 题目1408:吃豆机器人

    钱币兑换问题 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Sub ...

  5. 九度oj题目&amp;吉大考研11年机试题全解

    九度oj题目(吉大考研11年机试题全解) 吉大考研机试2011年题目: 题目一(jobdu1105:字符串的反码).    http://ac.jobdu.com/problem.php?pid=11 ...

  6. 九度oj 题目1007:奥运排序问题

    九度oj 题目1007:奥运排序问题   恢复 题目描述: 按要求,给国家进行排名. 输入:                        有多组数据. 第一行给出国家数N,要求排名的国家数M,国家号 ...

  7. 九度oj 题目1087:约数的个数

    题目链接:http://ac.jobdu.com/problem.php?pid=1087 题目描述: 输入n个整数,依次输出每个数的约数的个数 输入: 输入的第一行为N,即数组的个数(N<=1 ...

  8. 九度OJ题目1105:字符串的反码

    tips:scanf,cin输入字符串遇到空格就停止,所以想输入一行字符并保留最后的"\0"还是用gets()函数比较好,九度OJ真操蛋,true?没有这个关键字,还是用1吧,还是 ...

  9. 九度oj题目1009:二叉搜索树

    题目描述: 判断两序列是否为同一二叉搜索树序列 输入:                        开始一个数n,(1<=n<=20) 表示有n个需要判断,n= 0 的时候输入结束. 接 ...

  10. 九度oj题目1002:Grading

    //不是说C语言就是C++的子集么,为毛printf在九度OJ上不能通过编译,abs还不支持参数为整型的abs()重载 //C++比较正确的做法是#include<cmath.h>,cou ...

随机推荐

  1. [codility]CountDiv

    https://codility.com/demo/take-sample-test/count_div 此题比较简单,是在O(1)时间里求区间[A,B]里面能被K整除的数字,那么就计算一下就能得到. ...

  2. phantomjs使用说明

    phantomjs使用说明 12条评论   phantomjs实现了一个无界面的webkit浏览器.虽然没有界面,但dom渲染.js运行.网络访问.canvas/svg绘制等功能都很完备,在页面抓取. ...

  3. java服务器知识

    http://blog.csdn.net/chenyi8888/article/details/4484641 http://blog.csdn.net/chenyi8888/article/deta ...

  4. VS2005控制台程序修改nb0文件

    VS2005控制台程序修改nb0文件 我们要实现的功能就是通过CMD传递进来的值来在nb0文件末尾增加版本信息,新建控制台程序,自动生成的main函数如下,默认的代码非常简单: int _tmain( ...

  5. HDU 4483 Lattice triangle(欧拉函数)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4483 题意:给出一个(n+1)*(n+1)的格子.在这个格子中存在多少个三角形? 思路:反着想,所有情 ...

  6. [HIHO1051]补提交卡(枚举,贪心)

    题目链接:http://hihocoder.com/problemset/problem/1051 思路:先排序,然后枚举连续的长度为m的子段,用这个段之后的第一个天数减去这个段之前的第一个天数再-1 ...

  7. CRC校验码

    循环冗余校验码(CRC)的基本原理是:在K位信息码后再拼接R位的校验码,整个编码长度为N位,因此,这种编码也叫(N,K)码.对于一个给定的(N,K)码,可以证明存在一个最高次幂为R的多项式G(x)(R ...

  8. EJB3Persistence开发手册-原生SQL查询(NativeSQL)

    EJB3 QL对原生SQL做了非常好的支持.采用原生SQL做查询结果不但可以是象SQL中的返回列值,也可以是Entity类,甚至可以是两者的混合. EJB3 EntityManager接口定义了多个原 ...

  9. 宏buf_pool_struct

    typedef struct buf_pool_struct buf_pool_t; /** @brief The buffer pool structure. NOTE! The definitio ...

  10. uva580Critical Mass

    递推.   用f[i]代表i个盒子的放法,设g[i]=2^n-f[i],代表i个盒子不满足条件的放法. 枚举第一个U所在的位置j.则方法有g[j-2]*(2^(i-j-2))种,j-1必须是L. 所以 ...