题目链接:[CodeForces555B]Case of Fugitive[贪心][优先队列]
题意分析:依次排列成行的岛屿间需要假设桥梁,问:是否能在岛间都搭上桥?能,输出每个隔间需要的桥号。否则,输出"No"。
解题思路:贪心。按桥的长度升序排列,按岛屿间的最短距离island[i + 1].l - island[i].r升序排序。每次记录当前桥能搭上的岛屿数,然后把桥搭在这些岛屿中,岛屿间距离island[i + 1].r - island[i].l最小的那个上。
下面小小证明下这个贪心思路:当当前桥能搭上这些岛屿时,说明后面的桥都能满足大于这些岛屿最小间隔,但是不一定满足这些岛屿最大间隔的最小值。所以每次取出时我们尽量去选择最大间隔小的去放,这样就能使后期最大间隔的最小值变大。
个人感受:贪心贪心,多思考,多思考TAT
具体代码如下:
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
#include <string>
#include <cmath>
#include <map>
#include <set>
#include <algorithm>
typedef long long ll;
using namespace std;
const int MAXN = 2e5 + 111;
struct Island{
int id;
ll l, r;
bool operator < (const Island& t)const{
return r > t.r;
}
}island[MAXN];
bool cmp(const Island& a, const Island& b){
return a.l < b.l;
}
struct B{
ll len;
int id;
bool operator < (const B& t)const{
return len < t.len;
}
}b[MAXN];
int ans[MAXN];
int main() {
int n, m;
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; ++i)
scanf("%lld %lld", &island[i].l, &island[i].r);
for (int i = 1; i <= n - 1; ++i) //循环利用数组,将岛屿的左右转换为岛屿间的最小间隔与最大间隔~
{
ll mi = island[i + 1].l - island[i].r, mx = island[i + 1].r - island[i].l;
island[i].l = mi, island[i].r = mx;
island[i].id = i;
}
for (int i = 1; i <= m; ++i)
{
scanf("%lld", &b[i].len);
b[i].id = i;
}
sort(island + 1, island + n, cmp);
sort(b + 1, b + m + 1);
priority_queue<Island> q;
int cnt = 1, num = 0;
for (int i = 1; i <= m; ++i)
{
while (!q.empty() && q.top().r < b[i].len) //如果比当前桥长度都小,那么肯定比后面桥还短
q.pop();
while (cnt <= n - 1 && island[cnt].l <= b[i].len && b[i].len <= island[cnt].r) //能在岛间搭桥
q.push(island[cnt++]);
if (q.empty())
continue;
Island tem = q.top(); q.pop(); //取出能搭桥的岛中,r最小的
ans[tem.id] = b[i].id;
++num;
}
if (num == n - 1)
{
printf("Yes\n");
for (int i = 1; i < n - 1; ++i)
printf("%d ", ans[i]);
printf("%d\n", ans[n - 1]);
}
else puts("No");
return 0;
}