贪心法——区间覆盖问题

贪心法——区间覆盖问题

区间覆盖问题。数轴上有n个闭区间 [ai,bi] ,选择尽量少的区间覆盖一条指定线段 [s,t]

先进行预处理,将不包含 [s,t] 的区间都去掉,然后再按左区间从小到大排序。如果最小区间的左区间大于 s 的话,则无解。选取包含 s 的右区间最大的区间,该区间的右区间仍然小于下一个区间的左区间,则无解。按照这样的方法一直选择到最后一个区间,如果最后一个区间的右区间小于 t ,则无解。否则输出所有区间。

区间覆盖问题实现代码

// 区间数据结构
struct Region {
    // 左区间
    int left;
    // 右区间
    int right;
    operator < (const Region &r) {
        return left < r.left;
    }
};


// 贪心法
// 区间覆盖问题
void region(Region *r, int n, Region target) {
    // 预处理,将和target有交集的区间提取出来
    int num = 0;
    Region re[n];
    for(int i = 0; i < n; i++) {
        // 和目标线段无交集
        if(r[i].left > target.right || r[i].right < target.left) {
            continue;
        }
        re[num++] = r[i];
    }
    // 区间是否覆盖完全
    int isCover = 1;
    // 处理后无区间
    if(!num) {
        isCover = 0;
    }

    // 按左区间大小从小到大排序
    sort(re, re + num);

    // 当前最大区间
    Region curMax = re[0];
    // 记录覆盖区间
    vector<Region> coverR;

    // 处理覆盖最左边的问题
    if(curMax.left > target.left) {
        isCover = 0;
    }

    // 处理覆盖中间问题
    for(int i = 1; i < num; i++) {
        // 下一个区间和当前最大区间无交集
        if(re[i].left > curMax.right) {
            isCover = 0;
            break ;
        }
        // 覆盖右区间比当前最大区间大,且还能覆盖到目标区间左区间
        if(re[i].right > curMax.right && re[i].left <= target.left) {
            curMax.left = re[i].left;
            curMax.right = re[i].right;
        } else if(re[i].left > target.left) {
            coverR.push_back(curMax);
            // 目标区间左区间设置为最大区间右区间
            target.left = curMax.right;
            // 该区间覆盖右区间比最大右区间大
            if(re[i].right > curMax.right) {
                curMax.left = re[i].left;
                curMax.right = re[i].right;
            }
        }
    }

    // 处理覆盖最右边的问题
    if(curMax.right < target.right) {
        isCover = 0;
    } else {
        coverR.push_back(curMax);
    }

    // 输出
    if(isCover) {
        for(int i = 0; i < coverR.size(); i++) {
            cout << "区间:(" << coverR[i].left << "," << coverR[i].right << ")" << endl;
        }
        cout << "区间总数为:" << coverR.size() << endl << endl;
    } else {
        cout << "无解。" << endl << endl;
    }
}

测试主程序

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

using namespace std;

// 区间数据结构
struct Region {
    // 左区间
    int left;
    // 右区间
    int right;
    operator < (const Region &r) {
        return left < r.left;
    }
};


// 贪心法
// 区间覆盖问题
void region(Region *r, int n, Region target) {
    // 预处理,将和target有交集的区间提取出来
    int num = 0;
    Region re[n];
    for(int i = 0; i < n; i++) {
        // 和目标线段无交集
        if(r[i].left > target.right || r[i].right < target.left) {
            continue;
        }
        re[num++] = r[i];
    }
    // 区间是否覆盖完全
    int isCover = 1;
    // 处理后无区间
    if(!num) {
        isCover = 0;
    }

    // 按左区间大小从小到大排序
    sort(re, re + num);

    // 当前最大区间
    Region curMax = re[0];
    // 记录覆盖区间
    vector<Region> coverR;

    // 处理覆盖最左边的问题
    if(curMax.left > target.left) {
        isCover = 0;
    }

    // 处理覆盖中间问题
    for(int i = 1; i < num; i++) {
        // 下一个区间和当前最大区间无交集
        if(re[i].left > curMax.right) {
            isCover = 0;
            break ;
        }
        // 覆盖右区间比当前最大区间大,且还能覆盖到目标区间左区间
        if(re[i].right > curMax.right && re[i].left <= target.left) {
            curMax.left = re[i].left;
            curMax.right = re[i].right;
        } else if(re[i].left > target.left) {
            coverR.push_back(curMax);
            // 目标区间左区间设置为最大区间右区间
            target.left = curMax.right;
            // 该区间覆盖右区间比最大右区间大
            if(re[i].right > curMax.right) {
                curMax.left = re[i].left;
                curMax.right = re[i].right;
            }
        }
    }

    // 处理覆盖最右边的问题
    if(curMax.right < target.right) {
        isCover = 0;
    } else {
        coverR.push_back(curMax);
    }

    // 输出
    if(isCover) {
        for(int i = 0; i < coverR.size(); i++) {
            cout << "区间:(" << coverR[i].left << "," << coverR[i].right << ")" << endl;
        }
        cout << "区间总数为:" << coverR.size() << endl << endl;
    } else {
        cout << "无解。" << endl << endl;
    }
}

int main() {
    while(true) {
        // n个开区间
        int n;
        cout << "请输入闭区间的数量(0退出):";
        cin >> n;
        if(!n) {
            break;
        }
        // 需要覆盖的线段区间
        Region target;
        cout << "需要覆盖的线段区间为:";
        cin >> target.left >> target.right;

        Region r[n];
        for(int i = 0; i < n; i++) {
            cout << "第" << i + 1 << "个闭区间(x,y)为:";
            cin >> r[i].left;
            cin >> r[i].right;
        }

        cout << "最少区间能覆盖线段区间[" << target.left << "," << target.right <<"]组合和总数为:" << endl;
        region(r, n, target);
    }
    return 0;
}

输出数据

请输入闭区间的数量(0退出):5
需要覆盖的线段区间为:5 10
第1个闭区间(x,y)为:1 3
第2个闭区间(x,y)为:3 6
第3个闭区间(x,y)为:5 7
第4个闭区间(x,y)为:7 9
第5个闭区间(x,y)为:8 11
最少区间能覆盖线段区间[5,10]组合和总数为:
区间:(5,7)
区间:(7,9)
区间:(8,11)
区间总数为:3

请输入闭区间的数量(0退出):3
需要覆盖的线段区间为:5 9
第1个闭区间(x,y)为:1 3
第2个闭区间(x,y)为:1 4
第3个闭区间(x,y)为:2 3
最少区间能覆盖线段区间[5,9]组合和总数为:
无解。

请输入闭区间的数量(0退出):3
需要覆盖的线段区间为:5 9
第1个闭区间(x,y)为:10 11
第2个闭区间(x,y)为:12 13
第3个闭区间(x,y)为:14 15
最少区间能覆盖线段区间[5,9]组合和总数为:
无解。

请输入闭区间的数量(0退出):5
需要覆盖的线段区间为:5 9
第1个闭区间(x,y)为:6 7
第2个闭区间(x,y)为:7 8
第3个闭区间(x,y)为:8 9
第4个闭区间(x,y)为:9 10
第5个闭区间(x,y)为:10 11
最少区间能覆盖线段区间[5,9]组合和总数为:
无解。

请输入闭区间的数量(0退出):5
需要覆盖的线段区间为:5 9
第1个闭区间(x,y)为:1 5
第2个闭区间(x,y)为:5 6
第3个闭区间(x,y)为:5 7
第4个闭区间(x,y)为:8 9
第5个闭区间(x,y)为:9 10
最少区间能覆盖线段区间[5,9]组合和总数为:
无解。

请输入闭区间的数量(0退出):5
需要覆盖的线段区间为:5 9
第1个闭区间(x,y)为:1 5
第2个闭区间(x,y)为:5 6
第3个闭区间(x,y)为:5 7
第4个闭区间(x,y)为:5 8
第5个闭区间(x,y)为:6 7
最少区间能覆盖线段区间[5,9]组合和总数为:
无解。

请输入闭区间的数量(0退出):0

Process returned 0 (0x0)   execution time : 172.044 s
Press any key to continue.

你可能感兴趣的:(算法,算法竞赛,贪心法,区间覆盖问题)