[转]会场安排问题 南阳理工学院OJ 题目14

题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=14

经典问题。此题的最佳解法是用贪心算法:

1) 将所有输入活动按照结束时间从早到晚排序
2) 顺序遍历列表,将可以加入日程的活动加入,并维护活动计数
3) 遍历完毕后,输出活动计数
整个过程需要1) 将活动列表排序O(nlogn) 2) 遍历列表O(n),因此复杂度为O(nlogn)

 
   
#include < iostream >
#include < vector >
#include < utility >
#include < algorithm >
using namespace std;

bool Comp( const pair < int , int > A1, const pair < int , int > A2)
{
return A1.second < A2.second;
}

void greedySelect()
{

int start,finish;
int m;
cin >> m;
while (m -- ){
int n;
cin >> n;
vector < pair < int , int > > Avec;
vector < pair < int , int > > Svec;
while (n -- )
{
cin >> start >> finish;
Avec.push_back(make_pair(start,finish));
}
cin.clear();
sort(Avec.begin(),Avec.end(),Comp);
vector < pair < int , int > > ::iterator iter = Avec.begin();
Svec.push_back(make_pair(iter -> first,iter -> second));
finish = iter -> second;
for ( ++ iter;iter != Avec.end(); ++ iter)
if (iter -> first > finish)
{
Svec.push_back(make_pair(iter -> first,iter -> second));
finish = iter -> second;
}
cout << Svec.size() << endl;
}
}
int main()
{
greedySelect();
return 0 ;
}

以下是我ac的解法,没有标准解法优,在此只是存下来留个念想,其他人可以直接忽略


我的思路是:

1. 递归穷举(这个解法TLE,没什么价值,忽略)

2. 贪心(基于起始时间排序)

1) 对活动按照开始时间排序
2) 消去不可能活动(肯定安排不到的活动)

不可能活动的定义:在所有n个活动ai(i=1, 2 ... n)组成的集合S中,对于任意的ai∈S,若存在aj∈S(i≠j),使得b(aj)≤b(ai), e(aj)≥e(ai)(b,e代表开始时间/结束时间),则aj一定不在使得活动数最多的活动集合中,可以将其从S中删去。活动aj即为不可能活动。
举个例子,以下几个活动集(一行为一个集合)中,只有红字的活动需要考虑,其它都可以删除。
{3,4} {2,4} {1,4} {1,5}
{2,3} {1,3} {2,4} {2,3}

删除不可能活动最简单的方法就是二层嵌套循环,复杂度为O(n^2),为了降低复杂度,可以在第1) 步的排序上做一些工作。
例如可以对活动按照(b↑e↓) 进行排序,这样可以使得在初始的几重循环中删去尽量多的元素,从而降低一些时间复杂度。

3) 按照贪心方法安排活动

遍历列表,将所有能够加入的活动加入。遍历完毕后,输出活动计数。

算法需要一次排序(O(nlogn)),一次嵌套循环(O(n^2)),因此复杂度为O(n^2)

标准解法和我的贪心解法都可以得到ac,不过标准解法在时间上明显优于我的解法。

标准解法:时间260内存308
我的解法:时间800内存308

 
   
#include < iostream >
#include < vector >
#include < algorithm >

using namespace std;

typedef struct _AR{
int b;
int e;
}AR;

bool operator < ( const AR & a, const AR & b){
if (a.b == b.b) return a.e > b.e;
return a.b < b.b;
}

vector < AR > arv;

template < class T >
void reverse(vector < T > & v){
T temp;
for ( int i = 0 ; i < v.size() / 2 ; i ++ ){
temp = v[i];
v[i] = v[v.size() - i - 1 ];
v[v.size() - i - 1 ] = temp;
}
}

void reduce(vector < AR > & v){

reverse(v);

vector < AR > ::iterator it = v.begin();
vector < AR > ::iterator it2;
while (it != v.end()){
it2 = it;
it2 ++ ;
while (it2 != v.end()){
if (it2 -> e >= it -> e){
v.erase(it2);
}
else {
it2 ++ ;
}
}
it ++ ;
}

reverse(v);

}

int main()
{

int n;
cin >> n;
while (n -- ){
arv.clear();
int c;
cin >> c;
while (c -- ){
int b, e;
cin >> b >> e;
AR a;
a.b = b;
a.e = e;
arv.push_back(a);
}
sort(arv.begin(), arv.end());
reduce(arv);

int r = - 1 ;
int count = 0 ;
for (vector < AR > ::iterator it = arv.begin(); it != arv.end(); it ++ ){
if (it -> b > r){
r = it -> e;
count ++ ;
}
}

cout << count << endl;
}

return 0 ;
}

转载于:https://www.cnblogs.com/yinger/archive/2011/05/11/2043022.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值