UVA1146 Now or later 解题报告
有
n 架飞机需要着陆。 每架飞机都可以选择“早着陆”和“晚着陆”两种方式之一,且必须选择一种。 第i 架飞机的早着陆时间为E_i ,晚着陆时间为L_i ,不得在其他时间着陆。 你的任务是为这些飞机安排着陆方式,使得相邻两个着陆时间间隔的最小值(称为安全间隔)应尽量大。
n \leq 2000,0 \leq t \leq 10 ^ 7
解题思路:
二分 + 2-SAT
首先分析一下题面,观察到最小值最大这个点就首先考虑二分。
二分时间间隔
可以发现,对于一个飞机我们显然可以拆成两个点来处理,一个点表示早降落时间,一个点表示晚降落时间。
然后我们对于飞机两两考虑,我们记
则如果
上面的约束条件转化为连边就是
这题比较恶心的一点在于 UVA 原题卡了前向星,建议使用 vector
(UVA 上是默认开 -O2
的),以及点数至少要开到两倍的
代码:
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cctype>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
using namespace std;
#define ll long long
const int N = 1e4 + 10;
int dfn[N],low[N],c[N],cnt,num,n;
bool ins[N];
stack <int> s;
vector <int> G[N];
void tarjan(int x) {
dfn[x] = low[x] = ++cnt;
s.push(x);ins[x] = 1;
for (int i = 0;i < G[x].size();++i) {
int v = G[x][i];
if (!dfn[v]) {
tarjan(v);
low[x] = min(low[x],low[v]);
} else if (ins[v]) {
low[x] = min(low[x],dfn[v]);
}
}
if (dfn[x] == low[x]) {
int y;c[x] = ++num;
do {
y = s.top();s.pop();
c[y] = num;ins[y] = 0;
} while (x != y);
}
}
int a[N][2];
bool check(int cap) {
for (int i = 1;i <= 2 * n;++i) G[i].clear();
memset(dfn,0,sizeof dfn);
memset(low,0,sizeof low);
memset(c,0,sizeof c);
memset(ins,0,sizeof ins);
cnt = num = 0;
for (int i = 1;i <= n;++i) {
for (int qwq = 0;qwq <= 1;++qwq) {
for (int j = i + 1;j <= n;++j) {
for (int qaq = 0;qaq <= 1;++qaq) {
if (abs(a[i][qwq] - a[j][qaq]) < cap) {
G[i + n * qwq].push_back(j + n * (qaq ^ 1));
G[j + n * qaq].push_back(i + n * (qwq ^ 1));
}
}
}
}
}//重新建图的过程
for (int i = 1;i <= 2 * n;++i) if (!dfn[i]) tarjan(i);
for (int i = 1;i <= n;++i) {
if (c[i] == c[i+n]) return 0;
}
return 1;
}
void solve() {
memset(ins,0,sizeof ins);
tot = 0;
int l = 0,r = 0,ans = 0;
for (int i = 1;i <= n;++i) scanf("%d %d\n",&a[i][0],&a[i][1]),r = max(r,a[i][1]);
while (l < r - 1) {
int mid = (l + r) >> 1;
if (check(mid)) l = mid;
else r = mid;
}
cout << l << endl;
return ;
}
signed main() {
while (scanf("%d\n",&n) != EOF) solve();
return 0;
}
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。