一,凌乱的yyy / 线段覆盖 - 洛谷
思路:按结束时间从小到大排序,然后比较,当竞赛开始的时间大于上一个竞赛的结束时间就入选了。
因为是结束时间排序,所以第一个符合的开始时间就是最好的。
AC代码
#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int a[N];
struct contest
{
int begin;
int end;
}c[N];
bool cmp(contest a, contest b) {
return a.end < b.end;
}
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++) cin >> c[i].begin >> c[i].end;
sort(c + 1, c + 1 + n, cmp);
int last = -1,count=0;
for (int i = 1; i <= n; i++) {
if (c[i].begin >= last) {//等于结束时间也是可以的
count++;
last = c[i].end;
}
else continue;
}
cout << count<<endl;
return 0;
}
二,删数问题 - 洛谷
思路:(1)这道题数字有240位,且要对其进行删除数字操作,可以改为字符串存储。操作方便且不会溢出。
(2)贪心策略:从左往右找出极大值(包括左右端点),删除极大值,如此反复操作。
(3)数字中对0的特殊处理,如果0出现再数字前面,如0078,012,这样的直接删除0,防止后续贪心策略误判。
AC代码
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
//从前往后极大值
int main()
{
vector<char> s;
string str;
int m;
cin >> str >> m;
for (int i = 0; i < str.length(); i++) s.push_back(str[i]);
while (m--) {
while (s[0] == '0') s.erase(s.begin());//0出现再数字前面,直接删除0
if (s[0] > s[1]) s.erase(s.begin());//第一个数比第二个数大,则删第一个
else {//从前往后极大值
for (int i = 1; i < s.size(); i++) {
if (i == s.size() - 1) {//特判,防止越界
if (s[i] >= s[i - 1]) {
s.erase(s.begin() + i);
break;
}
}
else {
if (s[i] >= s[i - 1] && s[i] >= s[i + 1]) {
s.erase(s.begin() + i);
break;
}
}
}
}
}
if (s[0] == '0') {
int flag = 0;
s.erase(s.begin());
int n = s.size();
for (int i = 0; i < n; i++) {
if (flag == 0 && s[i] == '0') continue;
else {
cout << s[i];
flag = 1;
}
}
if (flag == 0) cout << "0";
}
else {
for (int i = 0; i < s.size(); i++) {
cout << s[i];
}
}
return 0;
}
三,[NOIP2007 普及组] 纪念品分组 - 洛谷
思路:将物品从小到大排序,然后使用双指针,是最大的和最小的放在一起(如果价格之和没有超出给定的整数)
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
const long long N = 3e4 + 5;
int a[N];
//20 20 30 50 60 70 80 90 90
int main()
{
int n, val;
cin >> val >> n;
for (int i = 0; i < n; i++) cin >> a[i];
sort(a, a + n);
int l = 0, r = n - 1;
int count = 0;
while (l <= r) {
if (a[l] + a[r] > val) {//价格之和大于给定的数,那么最大的数单独包装,右指针向左移
r--;
count++;
}
else {//价格之和小于给定的数,那么最大的和最小的包装在一起,两指针移动
count++;
l++; r--;
}
}
cout << count << endl;
return 0;
}
四,跳跳! - 洛谷
思路:倒序排序,将0(作为地面)插入数组,双指针,右边动,记录一次,左边动,记录一次。先对石头高度进行降序排序,然后使用双指针来进行贪心选择。每次选择最大和最小的石头进行跳跃,并计算体力值的增加。这样就能得到跳跃过程中耗费的最大体力值。
AC代码
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
// 定义比较函数,用于排序时候降序排列
bool cmp(int a, int b) {
return a > b;
}
// 主函数
int main()
{
int n, a[305];
cin >> n; // 输入石头的个数
for (int i = 0; i < n; i++) cin >> a[i]; // 输入每块石头的高度
a[n] = 0; // 在数组末尾添加一个高度为0的虚拟石头,便于处理边界情况
sort(a, a + n + 1, cmp); // 对石头高度进行降序排序,方便后面进行贪心选择
int l = 0, r = n; // 使用双指针l和r,l指向当前选择的石头中最小的,r指向最大的
long long sum = 0; // 用于记录跳跃所消耗的总体力值
// 贪心选择,每次选择最大和最小的石头进行跳跃,并计算体力值
while (l < r) {
sum += (a[r] - a[l]) * (a[r] - a[l]); // 计算从最大石头跳到最小石头的体力值
r--;
sum += (a[r] - a[l]) * (a[r] - a[l]); // 计算从次大石头跳到次小石头的体力值
l++;
}
cout << sum << endl; // 输出最终的总体力值
return 0;
}
五,[NOIP2002 提高组] 均分纸牌 - 洛谷
思路:设定从左往右的顺序,求出平均值然后遍历,如果多了就传递给下一堆,少了就让下一堆传过来(也就是传一个负数给下一堆)。
AC代码
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
const long long N = 1e2 + 5;
int a[N];
int main()
{
int n, sum = 0;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
sum += a[i]; // 累加每堆纸牌数量,计算总和
}
int val = sum / n; // 计算纸牌的平均数量
int count = 0; // 定义整数变量count,用于记录移动次数
// 遍历纸牌堆,将多出的纸牌移动到相邻的堆中,使得每堆纸牌数逐渐接近平均值
for (int i = 0; i < n - 1; i++) {
if (a[i] != val) { // 如果当前堆的纸牌数量与平均值不相等
count++; // 移动次数加1
a[i + 1] += a[i] - val; // 将多出的纸牌移动到下一堆中
}
}
cout << count; // 输出移动次数
}
六,贪心算法练习 - Virtual Judge
思路:按分值大的排序,将作业安排再当天,如果当天没空,则安排再前一天
AC代码
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e3 + 5;
struct work
{
int ddl; // 截止日期
int score; // 分值
int flag; // 标记作业是否被安排在某天
};
// 自定义排序函数,按照分值大的排在前面,如果分值相同,则按截止日期小的排在前面
bool cmp(work a, work b) {
if (a.score == b.score) return a.ddl > b.ddl;
else return a.score > b.score;
}
int main()
{
int T;
cin >> T;
while (T--) {
int n;
struct work a[N];
cin >> n;
int t_max = 0;
for (int i = 1; i <= n; i++) cin >> a[i].ddl; // 输入每个作业的截止日期
for (int i = 1; i <= n; i++) cin >> a[i].score; // 输入每个作业的分值
for (int i = 1; i <= n; i++) a[i].flag = 0; // 初始化作业的标记为0
sort(a + 1, a + 1 + n, cmp);
int t[N] = { 0 }; // 定义一个数组t用来记录每天是否有作业安排,初始化为0
for (int i = 1; i <= n; i++) { // 遍历每个作业
if (t[a[i].ddl] == 0) { // 如果当天没有作业安排
t[a[i].ddl] = 1; // 将当天标记为有作业安排
a[i].flag = 1; // 将当前作业标记为已经安排在当天
}
else { // 当天已经有作业安排
int count = a[i].ddl - 1; // 从前一天开始往前找可安排的天数
while (1) {
if (count <= 0) break; // 如果已经到第一天,就跳出循环
if (t[count] == 0) { // 如果前一天没有作业安排
a[i].flag = 1; // 将当前作业标记为已经安排在前一天
t[count] = 1; // 将前一天标记为有作业安排
break; // 跳出循环
}
count--; // 继续向前找
}
}
}
int ans = 0;
for (int i = 1; i <= n; i++) {
if (a[i].flag == 0) ans += a[i].score; // 如果作业未被安排,则累加其分值
}
cout << ans << endl; // 输出总的未安排作业的分值
}
return 0;
}
七,贪心算法练习 - Virtual Judge
思路:按 攻击/血量 的比值进行排序,先把比值大的干掉。
AC代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
struct hero
{
int hp;
int dps;
double rate;
};
// 自定义比较函数,用于结构体数组的快速排序,按照 rate 降序排序
int cmp(const void* a, const void* b)
{
return (*(struct hero*)b).rate > (*(struct hero*)a).rate ? 1 : -1;
}
// 先打 伤害/血量 比值高的,对方先出手
int main()
{
int n;
while (scanf("%d", &n) != EOF && n != 0) {
struct hero a[30];
for (int i = 0; i < n; i++) {
scanf("%d%d", &a[i].dps, &a[i].hp);
a[i].rate = 1.0 * a[i].dps / a[i].hp; // 计算每个英雄的伤害/血量比值
}
qsort(a, n, sizeof(a[0]), cmp); // 对英雄数组按照 rate 降序排序,这里可以用C++里的sort()排序,更方便
int sum = 0;
int count = 0;
while (a[n - 1].hp > 0) // 当最后一个英雄的血量大于 0 时,即还有敌方英雄存活,继续进行战斗
{
for (int i = 0; i < n; i++) {
if (a[i].hp > 0) sum += a[i].dps; // 计算本回合的总伤害,每个存活的英雄都会进行攻击
}
a[count].hp -= 1; // 将敌方当前轮到出手的英雄的血量减去 1,表示我方进行了攻击
if (a[count].hp == 0) count++; // 如果当前轮到出手的敌方英雄血量为 0,表示他已经死亡,更新轮到下一个出手的敌方英雄
}
printf("%d\n", sum); // 输出战斗过程中我方的总伤害
}
return 0;
}