CLRS 16.1活动选择问题

16.1-1
当活动 k 在集合活动 Sij 中时,必然有 i<k<j ,也就是说 ji2 ,同时还有 fisk,fksj 。我们将 k j1 开始递减,遇到 k=i 或者 fkfi (当 fkfi 则活动 i+1 到活动 k 的活动都不和活动 i 兼容)时就结束。
增加两个虚拟的活动 a0,f0=0an+1,sn+1= 。现在变成从活动 a0...an+1 中选择最大的可兼容集合 S0,n+1 。使用两个表 c[0..n+1,0..n+1]act[0..n+1,0..n+1] 分别记录活动个数和选择的活动 k 。当 ji<2 Sij= ,初始化 c[i,i]=0(i),c[i,i+1]=0(0in)

DYNAMIC-ACTIVITY-SELECTOR(s, f, n)
    let c[0..n + 1,0..n + 1] and act[0..n + 1,0..n + 1] be new tables
    for i = 0 to n
        c[i, i] = 0
        c[i, i + 1] = 0
    c[n + 1, n + 1] = 0
    for l = 2 to n + 1
        for i = 0 to n - l + 1
            j = i + l
            c[i, j]  = 0
            k = j - 1
            while f [i] < f [k]
                if f[i] ≤ s[k] and f[k] ≤ s[j]  and c[i, k] + c[k, j] + 1 > c[i, j] 
                    c[i, j] = c[i, k] + c[k, j] + 1
                    act[i, j]  = k
                k = k - 1
    print “A maximum size set of mutually compatible activities has size ” c[0, n + 1]
    print “The set contains ”
    PRINT-ACTIVITIES(c, act, 0, n + 1)

PRINT-ACTIVITIES(c, act, i, j)
    if c[i, j] > 0
        k = act[i, j ]
        PRINT-ACTIVITIES(c, act, i, k)
        PRINT-ACTIVITIES(c, act, k, j)
        print k

动态规划是 O(n3) ,贪心算法是 O(n)
附上实际运行代码:

#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::vector;

struct Activities
{
    int start_time;
    int finish_time;
    Activities():start_time(0),finish_time(0){}
};

void RECURSIVE_ACTIVITY_SELECTOR(Activities *act,int k,int n,vector<int> &activities)
{
    int m = k + 1;
    while(m < n && act[m].start_time < act[k].finish_time)
        m += 1;
    if(m < n)
    {
        activities.push_back(m);
        RECURSIVE_ACTIVITY_SELECTOR(act,m,n,activities);
    }
    else return;
}

void GREEDY_ACTIVITY_SELECTOR(Activities *act,int n,vector<int> &activities)
{
    activities.push_back(1);
    int k = 1;
    for(int m = 2; m < n; m++)
    {
        if(act[m].start_time >= act[k].finish_time)
        {
            k = m;
            activities.push_back(m);
        }
    }
}

void PRINT_ACTIVITY(int **c,int **activities,int i,int j)
{
    if(c[i][j] > 0)
    {
        int k = activities[i][j];
        PRINT_ACTIVITY(c,activities,i,k);
        PRINT_ACTIVITY(c,activities,k,j);
        cout << k << ' ';
    }
}

void DYNAMIC_ACTIVITY_SELECTOR(Activities *act,int n)
{
    int **c = new int *[n+2];
    int **activities = new int *[n+2];
    for(int i = 0; i <= n + 1; i++)
    {
        c[i] = new int[n+2];
        activities[i] = new int[n+2];
    }
    for(int i = 0; i <= n; i++)
    {
        c[i][i] = 0;
        c[i][i+1] = 0;
    }
    c[n+1][n+1] = 0;
    for(int l = 2; l <= n + 1; l++)
    {
        for(int i = 0; i <= n - l + 1; i++)
        {
            int j = i + l;
            c[i][j] = 0;
            int k = j - 1;
            while(act[i].finish_time < act[k].finish_time)
            {
                if(act[i].finish_time <= act[k].start_time && act[k].finish_time <= act[j].start_time && c[i][k]+c[k][j]+1 > c[i][j])
                {
                    c[i][j] = c[i][k] + c[k][j] + 1;
                    activities[i][j] = k;
                }
                k--;
            }
        }
    }
    cout << "A maximum size set of mutually compatible activities has size " << c[0][n+1] << endl;
    cout << "Using DYNAMIC_ACTIVITY_SELECTOR, the set contains ";
    PRINT_ACTIVITY(c,activities,0,n+1);

    for(int i = 0; i <= n + 1; i++){
        delete []c[i];
        delete []activities[i];
    }
    delete []c;
    delete []activities;
}

int main()
{
    int start[] = {0,1,3,0,5,3,5,6,8,8,2,12,INT_MAX};//添加两个虚拟活动
    int end[] = {0,4,5,6,7,9,9,10,11,12,14,16,INT_MAX};
    Activities *act = new Activities[13];
    for(int i = 0; i < 13; i++){
        act[i].start_time = start[i];
        act[i].finish_time = end[i];
    }
    vector<int> recursive,greedy;//保存结果
    //这里活动数是11个,加上虚拟的第0个结点,因此调用是12,下面的greedy调用同
    RECURSIVE_ACTIVITY_SELECTOR(act,0,12,recursive);
    GREEDY_ACTIVITY_SELECTOR(act,12,greedy);
    cout << "A maximum size set of mutually compatible activities has size " << recursive.size() << endl;
    cout << "Using RECURSIVE_ACTIVITY_SELECTOR, the set contains ";
    for(int i = 0; i < recursive.size(); i++)
        cout << recursive[i] << ' ';
    cout << endl << endl;

    cout << "A maximum size set of mutually compatible activities has size " << recursive.size() << endl;
    cout << "Using GREEDY_ACTIVITY_SELECTOR, the set contains ";
    for(int i = 0; i < recursive.size(); i++)
        cout << recursive[i] << ' ';
    cout << endl << endl;


    DYNAMIC_ACTIVITY_SELECTOR(act,11);//一共11个活动,这里调用是11
    cout << endl << endl;
    //注:使用动态规划得到的2,4,9,11,和贪心算法的答案不一样,但是都是对的
    delete []act;
    return 0;
}

16.1-2
设现在有 n 个活动 a1,a2,...,an

GREEDY-ACTIVITY-SELECTOR-JMC(s,f)
    n = s.length
    A = {a_n}
    for m=n-1 to 1
        if f[m]<=s[k] //greedy step
            A={a_m} U A
            k=m
    return A

证明类似书中定理16.1,在此略。

16.1-3
1) 持续时间最短

i123
开始时间023
结束时间346
持续时间323

选择持续时间最短应该选活动 2,实际上是选择活动 1、3。

2) 重叠最少者

i1234567891011
开始时间01112345556
结束时间23334567778
重叠个数34444244443

先选 6,再选 1、3。实际最优是 1、 5、 7、 11.

3) 最早开始
参考16.1中给的例子,然后我们在那个例子中添加一个活动 a12 ,开始结束时间分别是 0,14。选择这个活动就不能再选其他活动了。

16.1-4
S n 个活动的集合。
首先,最直观的是先选择最大可兼容的活动集合 S1 ,将集合 S1 中的活动放在第一个教室,然后从集合 SS1 中选择最大可兼容集合 S2 放在第二个教室….这个方法是 Θ(n2) 。但是有个问题是可能用到的教室会比实际确实需要的教室多,比如 {[1,4),[2,5),[6,7),[4,8)} 。按上述方法分别选出 {[1,4),[6,7)} 以及 [2,5) [4,8) ,需要三个教室,实际上选择 {[1,4),[4,8)} {[2,5),[6,7)} 只需两个教室。
换个思路,基本思想是按照开始时间顺序,将每个活动放入任意一个在那个时间可以使用的教室中。按时间顺序(包括开始和结束时间)建立一个活动集合,维护两个链表:在 t 时刻忙的教室和在 t 时刻闲的教室。当 t 时刻是某个活动开始的时间,将这个活动放入闲的教室并把这个闲教室放入忙教室链表中。当 t 时刻是某个活动的结束时间,将活动所在的教室从忙教室链表移动到闲教室链表。
为了尽可能减少教室使用量,如果可能的话,在选择从没使用过的教室前,总是先选择已经有过活动的教室。
时间复杂度: n 个活动有 2n 个时间点(开始和结束时间),需要 O(nlgn) ,扫描 2n 个时间点做 O(1) 的工作(将教室从一个链表移动到另一个),花费 O(n) 时间,总共是 O(n+timetosort)

16.1-5
此题不能用贪心算法,但是可以用动态规划。
按书中本节的定义定义一个活动集合 Sij ,一个最优的解是 Sij 中具有最大值的可兼容集合的子集。设 Aij Sij 的一个最优解,设 ak Aij 中,则 Aij=AikakAkj 。所以 Aij 的最大值等于 Aik 的最大值加 Akj 的最大值加 ak
记集合 Sij 的最优解的值是 val[i,j] 得: val[i,j]=val[i,k]+vk+val[k,j] 。由于我们不知道集合 Sij 中有活动 ak ,我们必须检查集合 Sij 中的所有活动然后决定选择哪一个。所以:

val[i,j]=0maxakSij{val[i,k]+val[k,j]+vk}if S_{ij}=∅if S_{ij}≠∅

在执行递归的时候,如果活动 k 在集合 Sij 中,则必有 i<k<j ,即 ji2 ,同时还有 fisk,fksj 。我们将 k j1 开始递减,遇到 k=i 或者 fkfi (当 fkfi 则活动 i+1 到活动 k 的活动都不和活动 i 兼容)时就结束。
增加两个虚拟的活动 a0,f0=0an+1,sn+1= 。现在变成从活动 a0...an+1 中选择可兼容集合具有最大值的 A0,n+1 。使用表 val[0..n+1,0..n+1]act[0..n+1,0..n+1] 分别记录递归值和选择放入集合 A0,n+1 的活动 k 。当 ji<2 Sij= ,初始化 val[i,i]=0(i),c[i,i+1]=0(0in)

MAX-VALUE-ACTIVITY-SELECTOR(s, f, v, n)
    let val[0..n + 1,0..n + 1] and act[0..n + 1,0..n + 1] be new tables
    for i = 0 to n
        val[i, i] = 0
        val[i, i + 1] = 0
    val[n + 1, n + 1] = 0
    for l = 2 to n + 1
        for i = 0 to n - l + 1
            j = i + l
            val[i, j]  = 0
            k = j - 1
            while f [i] < f [k]
                if f[i] ≤ s[k] and f[k] ≤ s[j]  and val[i, k] + val[k, j] + v_k > val[i, j] 
                    val[i, j] = val[i, k] + val[k, j] + v_k
                    act[i, j]  = k
                k = k - 1
    print “A maximum-value set of mutually compatible activities has valueval[0, n + 1]
    print “The set contains ”
    PRINT-ACTIVITIES(val, act, 0, n + 1)

PRINT-ACTIVITIES(val, act, i, j)
    if val[i, j] > 0
        k = act[i, j ]
        PRINT-ACTIVITIES(val, act, i, k)
        PRINT-ACTIVITIES(val, act, k, j)
        print k

时间复杂度是 O(n3) 。具体实现和练习16.1-1差不多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值