---恢复内容开始---
题意:
N个人想回家在至少一个时刻、至多两个时刻。并且,他们每个人都能独自回家。
定义:ai表示第i个人回家的时间, xij = abs(ai - aj) (i != j).
在每种时间计划中,定义 y = min{xij}。
问y最大可能是多少?
解法:
二分时间差的下界,用2-SAT判断是否可行。下确界即为所求。
代码:
这种是直接在跑的时候建边,是效率最快的

#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int MAXN = 2050; const int INF = (int)1e9+5; int a[MAXN]; bool mark[MAXN]; int S[MAXN], c; int n; inline int myAbs(int x) { return x>0 ? x : -x; } void init() { memset(mark, false, sizeof(mark)); memset(S, 0, sizeof(S)); c = 0; } bool dfs(int u, int k) { if (mark[u^1]) return false; //如果他的对立面和他同时被标记,那么这种情况不应该发生 if (mark[u]) return true; //如果他已经被标记 mark[u] = true; //他被标记 S[c++] = u; //这个点入栈 for (int i = 0; i < n*2; i++) if (u!=i && (u^1)!=i) { if (myAbs(a[u^1]-a[i])<k) { if (!dfs(i, k)) return false; } } return true; } bool check(int k) { init(); for (int i = 0; i < n*2; i += 2) { if (!mark[i] && !mark[i|1]) { //如果这个点没有被标记 c = 0; if (!dfs(i, k)) { //如果x不能赋值为真 while (c>0) mark[S[--c]] = false; if (!dfs(i|1, k)) return false; //如果x也不能赋值为假,那么无解 } } } return true; } void slove() { int l = 0, r = INF, m; while (l<r) { m = (l+r)>>1; if (!check(m)) r = m; else l = m+1; } printf("%d ", r-1); } int main() { #ifdef Phantom01 freopen("WHU1548.in", "r", stdin); #endif // Phantom01 while (scanf("%d", &n)!=EOF) { int k; for (int i = 0; i < n; i++) { scanf("%d %d", &k, &a[i<<1]); if (k&1) a[i<<1|1] = a[i<<1]; else scanf("%d", &a[i<<1|1]); } slove(); } return 0; }
下面是先建边,再跑2-SAT
数组存邻接表(本地随机了20组数据,跑了12秒, oj跑了3秒)

#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <vector> using namespace std; const int MAXN = 2050; const int INF = (int)1e9+5; int Edge[MAXN], next[MAXN*MAXN], to[MAXN*MAXN], used; int a[MAXN]; bool mark[MAXN]; int S[MAXN], c; int n; inline int myAbs(int x) { return x>0 ? x : -x; } inline void addEdge(int u, int v) { next[used] = Edge[u]; to[used] = v; Edge[u] = used++; } void init() { memset(Edge, -1, sizeof(Edge)); used = 0; memset(mark, false, sizeof(mark)); memset(S, 0, sizeof(S)); c = 0; } bool dfs(int u, int k) { if (mark[u^1]) return false; //如果他的对立面和他同时被标记,那么这种情况不应该发生 if (mark[u]) return true; //如果他已经被标记 mark[u] = true; //他被标记 S[c++] = u; //这个点入栈 for (int p = Edge[u]; p != -1; p = next[p]) if (!dfs(to[p], k)) return false; return true; } bool check(int k) { init(); for (int i = 0; i < n*2; i++) { for (int j = 0; j < i; j++) { if (i==j || i==(j^1)) continue; if (myAbs(a[i]-a[j])<k) { addEdge(i^1, j); addEdge(j^1, i); } } } for (int i = 0; i < n*2; i += 2) { if (!mark[i] && !mark[i|1]) { //如果这个点没有被标记 c = 0; if (!dfs(i, k)) { //如果x不能赋值为真 while (c>0) mark[S[--c]] = false; if (!dfs(i|1, k)) return false; //如果x也不能赋值为假,那么无解 } } } return true; } void slove() { int l = 0, r = INF, m; while (l<r) { m = (l+r)>>1; if (!check(m)) r = m; else l = m+1; } printf("%d ", r-1); } int main() { #ifdef Phantom01 freopen("WHU1548.in", "r", stdin); #endif // Phantom01 while (scanf("%d", &n)!=EOF) { int k; for (int i = 0; i < n; i++) { scanf("%d %d", &k, &a[i<<1]); if (k&1) a[i<<1|1] = a[i<<1]; else scanf("%d", &a[i<<1|1]); } slove(); } return 0; }
下面是vector存边的(本地跑了30秒)

#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <vector> using namespace std; const int MAXN = 2050; const int INF = (int)1e9+5; vector<int> Edge[MAXN]; int a[MAXN]; bool mark[MAXN]; int S[MAXN], c; int n; inline int myAbs(int x) { return x>0 ? x : -x; } void init() { for (int i = 0; i < n*2; i++) Edge[i].clear(); memset(mark, false, sizeof(mark)); memset(S, 0, sizeof(S)); c = 0; } bool dfs(int u, int k) { if (mark[u^1]) return false; //如果他的对立面和他同时被标记,那么这种情况不应该发生 if (mark[u]) return true; //如果他已经被标记 mark[u] = true; //他被标记 S[c++] = u; //这个点入栈 for (int i = 0; i < Edge[u].size(); i++) if (!dfs(Edge[u][i], k)) return false; return true; } bool check(int k) { init(); for (int i = 0; i < n*2; i++) { for (int j = 0; j < i; j++) { if (i==j || i==(j^1)) continue; if (myAbs(a[i]-a[j])<k) { Edge[i^1].push_back(j); Edge[j^1].push_back(i); } } } for (int i = 0; i < n*2; i += 2) { if (!mark[i] && !mark[i|1]) { //如果这个点没有被标记 c = 0; if (!dfs(i, k)) { //如果x不能赋值为真 while (c>0) mark[S[--c]] = false; if (!dfs(i|1, k)) return false; //如果x也不能赋值为假,那么无解 } } } return true; } void slove() { int l = 0, r = INF, m; while (l<r) { m = (l+r)>>1; if (!check(m)) r = m; else l = m+1; } printf("%d ", r-1); } int main() { #ifdef Phantom01 freopen("WHU1548.in", "r", stdin); #endif // Phantom01 while (scanf("%d", &n)!=EOF) { int k; for (int i = 0; i < n; i++) { scanf("%d %d", &k, &a[i<<1]); if (k&1) a[i<<1|1] = a[i<<1]; else scanf("%d", &a[i<<1|1]); } slove(); } return 0; }