贪心、脑洞-Moving Tables HDU - 1050
看上去是一道贪心题,实际上确实一道贪心题,甚至还可以是一道脑洞题
贪心
开始的想法
开始我以为这是一个活动安排问题(书上也确实是这么说的),为什么呢,因为我们希望时间最短,很自然的就希望每个十分钟都能搬尽可能多的桌子,而每次搬运桌子,一段走廊就会被占用,这相当于一个活动占用了一段的时间。在这种理解下,我便认为这是一个不断重复的活动安排问题,只要每次安排的任务都是尽可能多的,那最后时间就是尽可能少的。(活动安排问题的策略是每次完成不发生冲突的结束时间最早的活动)
错误的想法
最后代码告诉我我是错的,例如这组数据
1
5
1 4
2 6
7 8
5 10
9 11
为什么会错呢,感觉还是贪心目光短浅了,它能保证每次在当前条件下安排最多数量,但操作过程并不是无后效性的,也就是说先前的决定会影响后来的决定。如图所示(这里假定房号是经过处理的)。
正确的想法
正确的想法应该是优先安排起始房号小的,这样可以让每次安排的区间更加紧密。我还不会证明正确性(有dalao明白的话请告诉我QAQ)。我感觉可能是因为每个区间都得被安排到,所以尽量紧密的话可能可以提高整体的效率吧。如图所示
代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
struct node {
int l, r;
};
bool cmp(node a, node b) {//让起始房号小的在前面
if (a.l != b.l) return a.l < b.l;
else return a.r < b.r;
}//使用小于号,则小的在前面
int main(void) {
int T;
cin >> T;
node room[205];
bool vis[205];
while (T--) {
memset(room, 0, sizeof(room));
memset(vis, 0, sizeof(vis));
int n;
cin >> n;
room[n].l = 0;room[n].r = 0;
for (int i = 0; i < n; i++) {
int a, b;
cin >> a >> b;
if (a > b) swap(a, b);//统一方向
room[i].l = a % 2 == 0 ? a - 1 : a;//统一化处理
room[i].r = b % 2 == 0 ? b : b + 1;
}
sort(room, room + n, cmp);
int ans = 0, cnt = 0;
while (1) {
int i = 0;
while (vis[i]) i++;
vis[i] = 1;//用while跳过前面已安排的元素,选中一个初始元素,并标记
if (i == n) break;//当每个元素都被安排了,就退出
int now = room[i].r;
while(i++ < n){//扫描一遍所有元素,每次花十分钟
if (room[i].l > now && !vis[i]) {//错误:忘记加 && !vis[i]
vis[i] = 1;//错误:忘记标记已访问的元素
now = room[i].r;
}
}
ans += 10;
}
cout << ans << endl;
}
return 0;
}
p.s. 错误的思路和正确的思路的代码的差别就体现在cmp函数上。
脑洞
每次搬运可以看作是一段区间,我们只要求出区间的最大重叠数量,就是少需要花的搬运次数。
但由于本题房间的安排的特殊性,我们要做一个统一化的处理。
代码
#include <stdio.h>
#include <string.h>
#define SIZE 405
int main(void) {
int count[SIZE];
int i, j, testNum, n, max, from, to;
scanf("%d", &testNum);
while (testNum--) {
scanf("%d", &n);
memset(count, 0, sizeof(count));
for (i = 0; i < n; i++) {
scanf("%d%d", &from, &to);
if (from > to) {
int temp = from;
from = to;
to = temp;
}
from = from % 2 == 0 ? from - 1 : from;
to = to % 2 == 0 ? to : to + 1;
for (j = from; j <= to; j++) {
count[j]++;
}
}
max = 0;
for (i = 0; i < SIZE; i++) {
if (count[i] > max) {
max = count[i];
}
}
printf("%d\n", max * 10);
}
return 0;
}