题目链接
题意
蛮荒的西部经常发生枪击事件,某场枪击事件现场有n个人(2≤n≤100),给出每个人的坐标(0≤x,y≤1000000),以及他们听到的枪击顺序(‘S1 heard S2 firing before S3’),请帮助法官判定所有人的陈述是否成立以及能否唯一确定枪击者的顺序(没出现在陈述为S2、S3的人认为没开枪,并且越早开枪的人量刑越重),陈述有矛盾则输出IMPOSSIBLE,能唯一确定枪击者的顺序则按先后顺序输出其名字,不能唯一确定顺序则输出UNKNOWN。
分析
题目原文已经对sample做了解释,说要考虑声音的传播速度340m/s,由此可以想到构建差分约束求解:设第 i i i个人开枪的时刻为 t i t_i ti,对于陈述‘ S k S_k Sk heard S i S_i Si firing before S j S_j Sj’可写出不等式 t i + ( x i − x k ) 2 + ( y i − y k ) 2 340 < t j + ( x j − x k ) 2 + ( y j − y k ) 2 340 t_i+\frac{\sqrt{(x_i-x_k)^2+(y_i-y_k)^2}}{340}<t_j+\frac{\sqrt{(x_j-x_k)^2+(y_j-y_k)^2}}{340} ti+340(xi−xk)2+(yi−yk)2<tj+340(xj−xk)2+(yj−yk)2即 t i − t j < ( x j − x k ) 2 + ( y j − y k ) 2 − ( x i − x k ) 2 + ( y i − y k ) 2 340 t_i-t_j<\frac{\sqrt{(x_j-x_k)^2+(y_j-y_k)^2}-\sqrt{(x_i-x_k)^2+(y_i-y_k)^2}}{340} ti−tj<340(xj−xk)2+(yj−yk)2−(xi−xk)2+(yi−yk)2,还可以将声速拿掉,将差分约束简化为 t i ′ − t j ′ < ( x j − x k ) 2 + ( y j − y k ) 2 − ( x i − x k ) 2 + ( y i − y k ) 2 t_i^{'}-t_j^{'}<\sqrt{(x_j-x_k)^2+(y_j-y_k)^2}-\sqrt{(x_i-x_k)^2+(y_i-y_k)^2} ti′−tj′<(xj−xk)2+(yj−yk)2−(xi−xk)2+(yi−yk)2。
这和一般的差分约束不太一样,一般的差分约束不等式带等号,无解等价于有向图存在负权圈(跑一下Bellman Ford即可),这里差分约束不带等号,那么0权圈也是无解的。并且本题有解时还需要判断拓扑排序结果是否唯一,考虑顶点数n并不大(2≤n≤100),可以用Floyd算法预处理之后,有0权圈或负权圈(即w[i][i]≤0)则无解,有解时利用dp找最长路(权值为正的有向边不考虑,因为对应的不等式为 t i ′ − t j ′ < 正数 t_i^{'}-t_j^{'}<正数 ti′−tj′<正数,代表i可能在j之后开枪也可能在j之前开枪)并根据其长度是否等于开枪总人数确定拓扑排序结果是否唯一。
测试数据
给一份测试数据
AC 代码
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
#define N 102
int f[N], s[N], m, n; double w[N][N]; char names[N][22], t[22]; long long x[N], y[N];
int idx() {
cin >> t;
for (int i=0; i<n; ++i) if (!strcmp(names[i], t)) return i;
return n;
}
int dp(int u) {
if (f[u] > 0) return f[u];
f[u] = 1; s[u] = -1;
for (int v=0; v<n; ++v) if (w[u][v] <= 0. && 1 + dp(v) > f[u]) f[u] = 1 + f[v], s[u] = v;
return f[u];
}
void solve() {
cin >> n >> m;
for (int i=0; i<n; ++i) cin >> names[i] >> x[i] >> y[i], f[i] = 0;
for (int i=0; i<n; ++i) for (int j=0; j<n; ++j) w[i][j] = 1e9;
while (m--) {
int u = idx(), v, z; cin >> t; v = idx(); cin >> t >> t; z = idx(); f[v] = f[z] = 1;
w[v][z] = min(w[v][z], sqrt((x[z]-x[u])*(x[z]-x[u]) + (y[z]-y[u])*(y[z]-y[u])) -
sqrt((x[v]-x[u])*(x[v]-x[u]) + (y[v]-y[u])*(y[v]-y[u])));
}
for (int k=0; k<n; ++k) for (int i=0; i<n; ++i) for (int j=0; j<n; ++j) {
w[i][j] = min(w[i][j], w[i][k] + w[k][j]);
if (w[i][j] + w[j][i] <= 0.) {
cout << "IMPOSSIBLE" << endl;
return;
}
}
for (int i=m=0; i<n; ++i) if (f[i]) ++m, f[i] = 0;
for (int i=0; i<n; ++i) if (dp(i) == m) {
cout << names[i];
while (s[i] >= 0) cout << ' ' << names[i = s[i]];
cout << endl;
return;
}
cout << "UNKNOWN" << endl;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int t; cin >> t;
while (t--) solve();
return 0;
}