Codeforces Round #237 (Div. 2) C. Restore Graph(水构造)
题目大意
一个含有 n 个顶点的无向图,顶点编号为 1~n。给出一个距离数组:d[i] 表示顶点 i 距离图中某个定点的最短距离。这个图有个限制:每个点的度不能超过 k
现在,请构造一个这样的无向图,要求不能有自环,重边,且满足距离数组和度数限制,输出图中的边;如果无解,输出 -1
数据规模:1 ≤ k < n ≤ 105,0 ≤ d[i] < n
做法分析
第一眼做法:SPFA 或者 BFS,想了想,还是乱搞
根据 d 数组直接构造这个图,因为最短路具有最优子结构,所以,d[i] 为 0 的点只有一个,从 0 到 maxLen 的所有距离,都一定在 d 数组中出现过,然后就考虑度数限制。
显然,距离为 len + 1 的点是由距离为 len 的点到达的,于是,将所有的点按照距离分类。要尽量满足度数限制,每个点的度数就要尽量少,所以,距离相同的点之间不能有边。于是,构造出来的解中,所有的边 (u, v) 一定满足这样的性质:d[u] == d[v] + 1(假设 d[u] > d[v]),这让我想起了 ISAP 求最大流的 gap 优化
然后,怎么保证每个点都尽量满足度数限制呢?平均分配
将距离为 len + 1 的点平均分配到距离为 len 的点里面去,这样一定是最优的,如果这样都不满足度数限制,肯定无解了
参考代码
1 #include <iostream> 2 #include <vector> 3 #include <cstring> 4 #include <cstdio> 5 #include <algorithm> 6 7 using namespace std; 8 9 const int N = 100005; 10 11 int k, n, d[N]; 12 vector <int> len[N]; 13 vector < pair <int, int> > edge; 14 15 int main() { 16 scanf("%d%d", &n, &k); 17 for (int i = 0; i <= n; i ++) len[i].clear(); 18 for (int i = 1; i <= n; i ++) { 19 scanf("%d", &d[i]); 20 len[d[i]].push_back(i); 21 } 22 if (len[0].size() != 1) { 23 printf("-1\n"); 24 return 0; 25 } 26 int maxLen = n; 27 for (; len[maxLen].size() == 0; maxLen --); 28 for (int i = 0; i <= maxLen; i ++) { 29 if (len[i].size() == 0) { 30 printf("-1\n"); 31 return 0; 32 } 33 } 34 edge.clear(); 35 for (int i = 1; i <= maxLen; i ++) { 36 int cnt1 = (int)len[i - 1].size(); 37 int cnt2 = (int)len[i].size(); 38 int cnt = cnt2 / cnt1 + (cnt2 % cnt1 != 0); 39 if (cnt + (i - 1 != 0) > k) { 40 printf("-1\n"); 41 return 0; 42 } 43 for (int id1 = 0, id2 = 0; id1 < cnt1 && id2 < cnt2; id1 ++) { 44 for (; id2 < (id1 + 1) * cnt && id2 < cnt2; id2 ++) { 45 edge.push_back(make_pair(len[i - 1][id1], len[i][id2])); 46 } 47 } 48 } 49 int cnt = (int)edge.size(); 50 printf("%d\n", cnt); 51 for (int i = 0; i < cnt; i ++) { 52 printf("%d %d\n", edge[i].first, edge[i].second); 53 } 54 return 0; 55 }
题目链接