此文会不断更新
0 前言
贪心的题实在是把我虐得不轻,干脆做个专题出来
1 题目列表
1.1 最大不相交区间问题
题目: 看电视
"心路历程":
思路一;
1 对区间左端点由小到大排序
2 遍历,对每个区间左端点,尽量找离它最近的右端点
3 下一个右端点从>=上个区间左端点开始找
提交,错误
思考后发现问题出在第2步,即根本没法保证哪个右端点离当前区间左端点近,举例:
1 10
2 9
3 8
4 6
7 8
离1最近的右端点是6,排在很后面
因此,还不如按区间右端点从小到大排序!
思路二:
1 对区间右端点从小到大排序
2 遍历,对每个区间,如果>=上一次记录的右端点,结果就+1,并将上一次记录的右端点更新为当前右端点
提交,错误
猜测贪心策略不对,遂证明
证明如下:
如果这是错的,即在某次遍历cur中,不用找最小右端点,能获得更优的结果
那更优结果的这次遍历的右端点会更大,那下一次遍历的左端点就会更往右,有可能就会遗漏
所以"最优的"有可能遗漏,矛盾
所以我的思路是正确的
思路三:
既然策略正确,那提交不对就有几种可能了:
一:代码实现的问题
检查后排除
二:数组不够大的问题
觉得实在不太可能
三:有特殊数据,比如有区间左端点为负数
为此我调整了写法
四: 输入读入不对
果然,这是多组输入的题!错误原因多半就是它了!
提交后,终于过了
贴上最后代码:
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 100;
struct tv{
int start;
int end;
}arr[N];
bool cmp(tv a, tv b) {
return a.end < b.end;
}
int main() {
int n;
while (scanf("%d", &n), n != 0) { // 其实判断的是后面
for (int i=0; i<n; ++i) scanf("%d %d", &arr[i].start, &arr[i].end);
sort(arr, arr+n, cmp);
int cnt = 1, last = arr[0].end;
for (int i=1; i<n; ++i) {
if (arr[i].start < last) continue;
cnt += 1;
last = arr[i].end;
}
printf("%d\n", cnt);
}
return 0;
}
1.2 汽车加油问题
很恶心的一道题,需要考虑的特殊情况比较多,非常考验代码力,忍着牙疼写了半天代码把它A了:
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 500;
struct Station {
double price;
double dist;
}arr[N];
bool cmp(Station a, Station b) {
return a.dist < b.dist;
}
int main() {
double capacity, dist, gas_avg;
int n_station;
double tmp_price;
double tmp_dist;
scanf("%lf %lf %lf %d", &capacity, &dist, &gas_avg, &n_station);
for (int i=0; i<n_station; ++i) {
scanf("%lf %lf", &tmp_price, &tmp_dist);
if (tmp_dist > dist) continue;
arr[i].price = tmp_price;
arr[i].dist = tmp_dist;
}
sort(arr, arr+n_station, cmp);
double interval = capacity * gas_avg; // 两加油站之间可以有的最大间隔,超出了无法到达
double max_dist = 0;
bool accessable = true;
if (arr[0].dist != 0) {
accessable = false;
}else{
for (int i=1; i<n_station; ++i) {
if (arr[i].dist - arr[i-1].dist > interval) {
accessable = false;
max_dist = arr[i-1].dist + interval;
break;
}
}
if (accessable) {
if (arr[n_station-1].dist + interval < dist) { // 最后一个加油站离终点太远了也不行
accessable = false;
max_dist = arr[n_station-1].dist + interval;
}
}
}
if (!accessable) {
printf("The maximum travel distance = %.2lf\n", max_dist);
return 0;
}
double res = 0, cur_dist = 0, remain_gas = 0;
int p=0;
// 主要解决两个问题,当前站加多少油,怎么选择下一站
while (p < n_station) { // p指向当前加油的站点
if (p == n_station - 1) { // 特判一下特殊情况
if (remain_gas) {
// 如果油足够
if (remain_gas * gas_avg + cur_dist >= dist) {
break;
} else { // 油不够
res += (dist - cur_dist - remain_gas * gas_avg) / gas_avg * arr[p].price;
break;
}
} else {
res += (dist - cur_dist) / gas_avg * arr[p].price;
break;
}
}
int pnext = p+1, pi = pnext;
while (pi < n_station && arr[pi].dist - arr[p].dist <= interval) {
if (arr[pi].price < arr[p].price) { // 只要比当前站价钱便宜就行
pnext = pi;
break;
}
++pi;
}
if (arr[p].price > arr[pnext].price) { // 下一站更便宜, 买油只买到下一站就行了
if (remain_gas){
if (remain_gas * gas_avg + cur_dist >= arr[pnext].dist) {
remain_gas -= (arr[pnext].dist - arr[p].dist) / gas_avg;
}else{
res += (arr[pnext].dist - cur_dist - remain_gas * gas_avg) / gas_avg * arr[p].price;
remain_gas = 0;
}
} else {
res += (arr[pnext].dist - cur_dist) / gas_avg * arr[p].price;
}
} else { // 下一站价钱更贵
// 看一下[下下站](如果有的话)
if (pnext == n_station - 1) {
// 买够油(买满或者买到撑到最后)
double need_capacity = (dist - cur_dist) / gas_avg;
if (need_capacity >= capacity) {
// 把油箱买满
res += (capacity - remain_gas) * arr[p].price;
remain_gas = capacity - (arr[pnext].dist - cur_dist) / gas_avg;
} else {
res += (need_capacity - remain_gas) * arr[p].price;
remain_gas = 0;
}
} else { // pnext不是最后一站,pnext2有可能是
int pnext2 = pnext+1, pi2 = pnext2;
while (pi2 < n_station && arr[pi2].dist - arr[pnext].dist <= interval) {
if (arr[pi2].price < arr[pnext].price) {
pnext2 = pi2;
break;
}
++pi2;
}
if (arr[pnext2].price < arr[p].price) { // 如果下下站更便宜
// 买油买到下下站或者加满
if (arr[pnext2].dist - arr[p].dist >= interval) {
res += (capacity - remain_gas) * arr[p].price;
remain_gas = capacity - (arr[pnext].dist - arr[p].dist) / gas_avg;
} else {
res += ((arr[pnext2].dist - arr[p].dist) / gas_avg - remain_gas) * arr[p].price;
remain_gas = (arr[pnext2].dist - arr[pnext].dist) / gas_avg;
}
} else { // 越来越贵, 那就买够油
if (arr[p].dist + interval >= dist) {
res += ((dist - arr[p].dist) / gas_avg - remain_gas) * arr[p].price;
remain_gas = (dist - arr[pnext].dist) / gas_avg;
} else {
res += (capacity - remain_gas) * arr[p].price;
remain_gas = capacity - (arr[pnext].dist - arr[p].dist) / gas_avg;
}
}
}
}
// 转移到下一站
p = pnext;
cur_dist = arr[p].dist;
}
printf("%.2lf\n", res);
return 0;
}
1.3 多个数字串拼接为最小数字串问题
此题比较经典
Recover the Smallest Number
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
const int N = 1e4;
bool cmp(string a, string b) { return a + b < b + a; }
string str[N];
int main() {
int n, p = 0;
scanf("%d", &n);
for (int i = 0; i < n; ++i) cin >> str[i]; // cin不会读入空格, scanf会
sort(str, str + n, cmp);
while (p < n && stoi(str[p]) == 0) ++p;
if (p < n) {
cout << stoi(str[p]);
for (int i=0; i<n; ++i) if (i != p) cout << str[i];
} else cout << 0;
return 0;
}
难处有二:
1 对于排序,不能用字典序比较,而要用两种拼接方式的字典序比较(比较巧妙)
2 只有第一个0开头的串需要去掉0,剩下的都不能去掉0, 需要仔细认真反复读题