您的位置:首页 > 其它

nyoj 720 项目安排(dp+二分优化)

2016-08-20 17:05 190 查看


项目安排

时间限制:3000 ms  |  内存限制:65535 KB
难度:4

描述小明每天都在开源社区上做项目,假设每天他都有很多项目可以选,其中每个项目都有一个开始时间和截止时间,假设做完每个项目后,拿到报酬都是不同的。由于小明马上就要硕士毕业了,面临着买房、买车、给女友买各种包包的鸭梨,但是他的钱包却空空如也,他需要足够的money来充实钱包。万能的网友麻烦你来帮帮小明,如何在最短时间内安排自己手中的项目才能保证赚钱最多(注意:做项目的时候,项目不能并行,即两个项目之间不能有时间重叠,但是一个项目刚结束,就可以立即做另一个项目,即项目起止时间点可以重叠)。

输入输入可能包含多个测试样例。

对于每个测试案例,输入的第一行是一个整数n(1<=n<=5000):代表小明手中的项目个数。

接下来共有n行,每行有3个整数st、ed、val,分别表示项目的开始、截至时间和项目的报酬,相邻两数之间用空格隔开。

st、ed、value取值均在32位有符号整数(int)的范围内,输入数据保证所有数据的value总和也在int范围内。
输出对应每个测试案例,输出小明可以获得的最大报酬。
样例输入
3
1 3 6
4 8 9
2 5 16
4
1 14 10
5 20 15
15 20 8
18 22 12


样例输出
16
22


提示
上传时数据加强,项目起始时间和终止时间可能相同(其他oj可能无此情况)

解题思路:
这个题很想之前做的会场安排问题,利用贪心求解,但这里需要考虑到花费,所以贪心并不能找到最优的。这里采用的是动态规划的思想,由于每个项目是按照时间来的,所以我们先将项目都按照结束时间排序,相同则按照开始时间小的排序。
首先讲下为什么要这么排序,其实这样是可以帮助我定义状态,dp[i]表示做完前i个项目所需要的最大价值。如果我们要表示做完这个项目,很明显的就是要找这个项目做完的时间。接下来就是如何找到在这个项目开始前所做的项目呢?我们可以这么思考,比这个项目更早的时间做完,那么它们的结束时间肯定当前这个项目的开始时间要小,这样我们就可以找到所有符合要求的项目了。
如果这里用枚举肯定会超时,可以采用二分优化,因为我们已经将项目排好序了,符合单调性。
只不过这里的提示纠结了好久,它说可能起始时间和终止时间相同,我在想会不会有多个项目是用一个时间段,然后我又想了去重,结果超时了。。最后把去重的函数去掉就A了。。

AC:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn = 5005;
struct Node
{
int st,ed,val;

bool operator < (const Node &rhs) const
{
if(st == rhs.st && ed == rhs.ed) return val > rhs.val;
else if(ed == rhs.ed) return st < rhs.st;
return ed < rhs.ed;
}
}p[maxn];
int n,dp[maxn];

int search(int l,int r,int key)
{
int mid,ans = 0;
while(l <= r)
{
mid = (l + r) >> 1;
if(p[mid].ed <= key)
{
ans = mid;
l = mid + 1;
}
else r = mid - 1;
}
return ans;
}

int main()
{
while(scanf("%d",&n)!=EOF)
{
for(int i = 1; i <= n; i++)
scanf("%d %d %d",&p[i].st,&p[i].ed,&p[i].val);
sort(p+1,p+1+n);
dp[1] = p[1].val;
for(int i = 2; i <= n; i++)
{
dp[i] = max(p[i].val,dp[i-1]); //只做这个项目,不做这个项目
int k = search(1,i-1,p[i].st);
dp[i] = max(dp[i],dp[k] + p[i].val);
}
printf("%d\n",dp
);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dp