东华大学高级程序设计上机题(贪心篇)

目录

  • 贪心
    • 有序矩阵中的第k个最小数组和
      • 题目
      • 代码
    • 买卖股票的最佳时机
      • 题目
      • 代码
    • 救生艇
      • 题目
      • 代码
    • 去除重复字母
      • 题目
      • 代码
    • 无重叠区间
      • 题目
      • 代码
    • 分割数组为连续子序列
      • 题目
      • 代码
    • 翻转矩阵后的得分
      • 题目
      • 代码
    • 拼接最大数
      • 题目
      • 代码
    • 按要求补齐数组
      • 题目
      • 代码
    • 设置交集大小至少为2
      • 题目
      • 代码
  • 后续内容持续更新~~~



贪心

有序矩阵中的第k个最小数组和

题目

给你一个 m * n 的矩阵 mat,以及一个整数 k ,矩阵中的每一行都以非递减的顺序排列。
你可以从每一行中选出 1 个元素形成一个数组。返回所有可能数组中的第 k 个 最小 数组和。

代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
//优先队列默认是最大堆 维护一个k大根堆 找到第k个最小数组和
class Solution {
public:
    vector<vector<int>>temp;
    int m, n;
    int kthSmallest(vector<vector<int>>& mat, int k) {
        temp = mat;
        m = mat.size(), n = mat[0].size();
        int left = 0, right = 0;
        for (int i = 0; i < m; i++) left += mat[i][0], right += mat[i].back();
        int init = left;
        while (left < right) {
            int mid = (left + right) >> 1;
            int num = 1;
            dfs(mid, 0, init, num, k);
            if (num >= k) right = mid;
            else left = mid + 1;
        }
        return left;
    }
    void dfs(int mid, int index, int sum, int& num, int k) {
        if (sum > mid || index == m || num > k) return;
        dfs(mid, index + 1, sum, num, k);
        for (int i = 1; i < n; i++) {
            if (sum + temp[index][i] - temp[index][0] <= mid) {
                num++;
                dfs(mid, index + 1, sum + temp[index][i] - temp[index][0], num, k);
            }
            else {
                break;
            }
        }
    }
};
int main() {
    int m, n, data, k;
    vector<vector<int> > mat;//二维数组
    cin >> m >> n;
    for (int i = 0; i < m; i++)
    {
        vector<int> row;
        for (int j = 0; j < n; j++)
        {
            cin >> data;
            row.push_back(data);
        }
        mat.push_back(row);
    }
    cin >> k;
    int res = Solution().kthSmallest(mat, k);
    cout << res << endl;
    return 0;


}


买卖股票的最佳时机

题目

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

代码

#include 
#include 
#include 
using namespace std;
/*
* 数组的第 i 个元素是一支给定股票第 i 天的价格。
你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)

*/
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        //计算最大利润
        //贪心思想:在价格最低的时候买入 在价格最高的时候卖出
        //遍历数组,找到最低价格,找到最高价格
        //可以完成多笔交易但不可以同时交易
        int mprofit = 0;
        for (int i = 1; i < prices.size(); i++) {
            int temp = prices[i] - prices[i - 1];
            if (temp > 0) mprofit += temp;
        }
        return mprofit;
    }
};
int main()
{
    int n, data;
    vector<int> prices;
    cin >> n;
    for (int i = 0; i < n; i++)
    {
        cin >> data;
        prices.push_back(data);
    }
    int res = Solution().maxProfit(prices);
    cout << res;
    return 0;
}

救生艇

题目

第 i 个人的体重为 people[i],每艘船可以承载的最大重量为 limit。
每艘船最多可同时载两人,但条件是这些人的重量之和最多为 limit。
返回载到每一个人所需的最小船数。(保证每个人都能被船载)。

代码

#include 
#include 
#include 
using namespace std;

int main() {
	//第i个人的体重为people[i] 每艘船可以承载的最大重量为limit
	//每艘船最多可同时载两人,但条件是这些人的重量之和最多为 limit。

	//返回载到每一个人所需的最小船数。(保证每个人都能被船载)。
	vector<int> people;
	int limit=0;
	int n=0;
	cin >> n;
	for (int i = 0; i < n; i++) {
		int temp;
		cin >> temp;
		people.push_back(temp);
	}
	cin >> limit;//3
	sort(people.begin(), people.end());//1 2 2 3 i:0 j:3 ,i:1 j:2
	int count = 0;//统计船的个数
	int num=people.size();//记录还有几个人
	int i = 0;
	int j = people.size() - 1;
	while (1) {
		if (num == 0||num==1) {
			break;
		}
		if (people[i] + people[j] <= limit) {
			i++;
			j--;
			num-=2;
		}
		else if (people[i] + people[j] > limit) {
			j--; //让重量大的单独坐船走
			num--;
		}
		count++;
	}
	if (num == 0) {
		cout << count;
	}else if (num == 1) {
		cout << count + 1;
	}
	
	return 0;
}

去除重复字母

题目

给你一个仅包含小写字母的字符串,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证返回结果的字典序最小(要求不能打乱其他字符的相对位置)。

代码

#include 
#include 
#include 
using namespace std;
//给你一个仅包含小写字母的字符串,请你去除字符串中重复的字母,使得每个字母只出现一次。
//需保证返回结果的字典序最小(要求不能打乱其他字符的相对位置)。
int main() {
	string s = "";
	cin >> s;
    string stk;
    size_t i = 0;
    for (size_t i = 0; i < s.size(); ++i)
    {
        if (stk.find(s[i]) != string::npos) continue;
        while (!stk.empty() && stk.back() > s[i] &&
            s.find(stk.back(), i) != string::npos)
            stk.pop_back();
        stk.push_back(s[i]);
    }
    cout<<stk;
	return 0;
}

无重叠区间

题目

给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。

代码

#include 
#include 
#include 
using namespace std;
//给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。
//可以认为区间的终点总是大于它的起点。

//区间[1, 2] 和[2, 3] 的边界相互“接触”,但没有相互重叠。
bool compare(vector<int>& a, vector<int>& b) {
    return a[1] < b[1]; // 按照区间结束时间从小到大排序
}

int main() {
    int n;
;
    cin>>n;

    vector<vector<int>> intervals(n, vector<int>(2, 0));

    for (int i = 0; i < n; ++i) {
        cin>>intervals[i][0]>>intervals[i][1];
    }

    if (intervals.empty()) {
        cout << 0;
        return 0;
    }
    sort(intervals.begin(), intervals.end(), compare);//按照区间结束时间从小到大排序

    int count = 1; // 计数器,初始化为1,表示至少需要保留一个区间
    int end = intervals[0][1]; // 当前保留第一个区间的结束时间

    //从第二个区间开始比较
    for (int i = 1; i < intervals.size(); ++i) {
        if (intervals[i][0] >= end) {
            // 当前区间的起始时间大于等于前一个区间的结束时间,不重叠
            count++;
            end = intervals[i][1];
        }
        // 如果重叠,不做任何操作
    }

    cout<<intervals.size() - count; // 需要移除的区间数量

    return 0;
}

分割数组为连续子序列

题目

给你一个按升序排序的整数数组 num(可能包含重复数字),请你将它们分割成一个或多个子序列,其中每个子序列都由连续整数组成且长度至少为 3 。
一个子序列是从原始数组挑选一部分(也可以全部)元素而不改变相对位置形成的新数组
如果可以完成上述分割,则返回 true ;否则,返回 false 。

代码

#include  
using namespace std;  
int n;  
  
class Solution {  
public:  
    bool isPossible(vector<int>& nums) {  
        map<int, int> numCounts, seqCounts; // numCounts用于记录数字的出现次数,seqCounts用于记录当前序列中的数字计数  
        for (auto num : nums) numCounts[num]++; // 记录所有数字的出现次数  
        for (auto num : nums) {  
            if (!numCounts[num]) continue; // 如果这个数已经用完了,就跳过本轮循环  
            if (seqCounts[num - 1]) { // 如果以num-1结尾的序列存在,就把num加到该序列的末尾  
                seqCounts[num]++;  
                seqCounts[num - 1]--;  
                numCounts[num]--;  
            } else if (numCounts[num + 1] && numCounts[num + 2]) { // 若num-1的序列不存在,尝试创建以num开头,num+2结尾的新序列  
                seqCounts[num + 2]++; // 创建一个新序列  
                numCounts[num]--, numCounts[num + 1]--, numCounts[num + 2]--;  
            } else {  
                return false; // 无法形成连续序列  
            }  
        }  
        return true;  
    }  
};  
  
int main() {  
    cin >> n;  
    vector<int> numbers; // 将v更改为numbers,以提高变量名的描述性  
    int value;  
    for (int i = 0; i < n; i++) {  
        cin >> value;  
        numbers.push_back(value);  
    }  
  
    if (Solution().isPossible(numbers)) puts("true");  
    else puts("false");  
  
    return 0;  
}

翻转矩阵后的得分

题目

有一个二维矩阵 A ,其中每个元素的值为 0 或 1 。
翻转是指选择任一行或列,并转换该行或列中的每一个值:将所有 0 都更改为 1,将所有 1 都更改为 0。
在做出任意次数的翻转后,将该矩阵的每一行都按照二进制数来解释,矩阵的得分就是这些数字的总和。
返回尽可能高的分数。

代码

#include 
#include 
#include 
#include 
#include 
using namespace std;

/*有一个二维矩阵 A ,其中每个元素的值为 0 或 1 。
翻转是指选择任一行或列,
并转换该行或列中的每一个值:将所有 0 都更改为 1,将所有 1 都更改为 0。
在做出任意次数的翻转后,将该矩阵的每一行都按照二进制数来解释,矩阵的得分就是这些数字的总和。
返回尽可能高的分数。*/
int main() {
    int m, n;
;
    cin >> m >> n;
    vector<vector<int>> A(m, vector<int>(n));
    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < n; ++j) {
            cin >> A[i][j];
        }
    }

    if (A.empty())
        return 0;
    int res = 0;
    int row = A.size(), col = A[0].size();
    // 逐行判断,将首位为0的行进行翻转操作
    for (int i = 0; i < row; ++i) {
        if (A[i][0] == 0) {
            for (int j = 0; j < col; ++j) {
                // 进行翻转操作
                A[i][j] = (1 - A[i][j]);
            }
        }
    }
    // 从第二列开始逐列判断是否需进行翻转操作,在判断是否
    // 需要进行行翻转操作时,保证了第一列必全为1
    for (int j = 1; j < col; ++j) {
        // 设置变量统计该行0的个数
        int cnt = 0;
        for (int i = 0; i < row; ++i) {
            if (A[i][j] == 0) {
                ++cnt;
            }
        }
        // 若该列中0个数多余1个数,做列翻转操作
        if (cnt > (row - cnt)) {
            for (int i = 0; i < row; ++i) {
                A[i][j] = (1 - A[i][j]);
            }
        }
    }
    // 得最后二进制结果
    for (int i = 0; i < row; ++i) {
        for (int j = col - 1; j >= 0; --j) {
            res += A[i][j] * int(pow(2, col - 1 - j));
        }
    }
    cout<< res;


	return 0;
}

拼接最大数

题目

给定长度分别为 m 和 n 的两个数组,其元素由 0-9 构成,表示两个自然数各位上的数字。现在从这两个数组中选出 k (k <= m + n) 个数字拼接成一个新的数,要求从同一个数组中取出的数字保持其在原数组中的相对顺序。
求满足该条件的最大数。结果返回一个表示该最大数的长度为 k 的数组。
说明: 请尽可能地优化你算法的时间和空间复杂度。

代码

#include 
#include 
#include 
#include 
#include 
using namespace std;
class Solution {
public:
    vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
        int n1 = nums1.size();
        int n2 = nums2.size();
        vector<int> res;
        for (int i = max(0, k - n2); i <= min(k, n1); i++) {
            res = max(res, mergeVector(maxVector(nums1, i), maxVector(nums2, k - i)));
        }

        return res;
    }
private:
    vector<int> maxVector(vector<int> nums, int k) {
        int drop = (int)nums.size() - k;
        vector<int> monDecStk;
        for (int num : nums) {
            while (drop > 0 && !monDecStk.empty() && num > monDecStk.back()) {
                monDecStk.pop_back();
                drop--;
            }
            monDecStk.push_back(num);
        }

        monDecStk.resize(k);
        return monDecStk;
    }

    vector<int> mergeVector(vector<int> nums1, vector<int> nums2) {
        vector<int> res;
        while (!nums1.empty() || !nums2.empty()) {
            auto& bigger = (nums1 > nums2) ? nums1 : nums2;
            res.push_back(bigger[0]);
            bigger.erase(bigger.begin());
        }

        return res;
    }
};
/*给定长度分别为 m 和 n 的两个数组,
其元素由 0-9 构成,表示两个自然数各位上的数字。
现在从这两个数组中选出 k (k <= m + n) 个数字拼接成一个新的数,
要求从同一个数组中取出的数字保持其在原数组中的相对顺序。
求满足该条件的最大数。结果返回一个表示该最大数的长度为 k 的数组。
说明: 请尽可能地优化你算法的时间和空间复杂度。*/
int main() {
    int m, n, k, data;

    vector<int> nums1, nums2;

    cin >> m;

    for (int i = 0; i < m; i++)

    {

        cin >> data;

        nums1.push_back(data);

    }

    cin >> n;

    for (int i = 0; i < n; i++)

    {

        cin >> data;

        nums2.push_back(data);

    }

    cin >> k;

    vector<int> res = Solution().maxNumber(nums1, nums2, k);

    for (int i = 0; i < res.size(); i++)

        cout << res[i];



    return 0;
}

按要求补齐数组

题目

给定一个已排序的正整数数组 nums,和一个正整数 n 。从 [1, n] 区间内选取任意个数字补充到 nums 中,使得 [1, n] 区间内的任何数字都可以用 nums 中某几个数字的和来表示。请输出满足上述要求的最少需要补充的数字个数。

代码

#include 
#include 
#include 
#include 
#include 
using namespace std;

/*给定一个已排序的正整数数组 nums ,和一个正整数 n 。
从 [1, n] 区间内选取任意个数字补充到 nums 中,
使得 [1, n] 区间内的任何数字都可以用 nums 中某几个数字的和来表示。
请返回 满足上述要求的最少需要补充的数字个数 。*/
int main() {
	int m;
	cin >> m;
	//输入m个整数
	vector<int> nums(m);
	for (int i = 0; i < m; i++) {
		cin >> nums[i];
	}
	int n;
	cin >> n;
    // 初始区间为空,表示已经覆盖
    long long x = 1;
    int index = 0;
    int times = 0;
    while (x <= n) {
        if (index < nums.size() && nums[index] < x) {
            x += nums[index++];
        }
        else if (index < nums.size() && nums[index] == x) {
            x += nums[index++];
        }
        else {
            x <<= 1;
            times++;
        }
    }
    cout<< times;
	return 0;
}

设置交集大小至少为2

题目

一个整数区间 [a, b] ( a < b ) 代表着从 a 到 b 的所有连续整数,包括 a 和 b。
给你一组整数区间intervals,请找到一个最小的集合 S,使得 S 里的元素与区间intervals中的每一个整数区间都至少有2个元素相交。
输出这个最小集合S的大小。

代码

#include 
#include 
#include 
#include 
#include 
using namespace std;
class Solution {
public:
    int intersectionSizeTwo(vector<vector<int>>& intervals) {
        // 排序
        sort(intervals.begin(), intervals.end(), [](const auto& a, const auto& b) {
            return a[1] == b[1] ? (a[0] > b[0]) : (a[1] < b[1]);
            });
        int e = -1, s = -1, res = 0;
        for (auto& interval : intervals) {
            if (interval[0] > e) {
                e = interval[1];
                s = e - 1;
                res += 2;
            }
            else if (interval[0] > s) {
                s = e;
                e = interval[1];
                ++res;
            }
        }
        return res;
    }
};

int main() {
    int m, n, data;

    vector<vector<int> > intervals;

    cin >> m;

    for (int j = 0; j < m; j++)

    {

        vector<int> aRow;

        for (int i = 0; i < 2; i++)

        {

            cin >> data;

            aRow.push_back(data);

        }

        intervals.push_back(aRow);

    }



    int res = Solution().intersectionSizeTwo(intervals);

    cout << res;



    return 0;
}

后续内容持续更新~~~

你可能感兴趣的:(算法,数据结构,面试,c++)