路程
路程
题意
构造一个点数不超过 \(18\),边数不超过 \(45\) 的有向无环图,满足:
入度为 \(0\) 的点只有 \(1\),出度为 \(0\) 的点只有 \(114\);
从 \(1\) 到 \(114\) 的所有路径的边权和恰好为 \([0,n]\)。
思路
先想到可以把每个数二进制拆分了,每一位对应 DAG 上的一层。
可以写出一个类似于数位 dp 的记忆化搜索。
\(f_x\) 表示构造出后 \(x\) 位即 \([0,2^x-1]\) 的 DAG 的起点。
搜索时顺便加边即可。
这样构造并不优秀,最大可能有 \(20\) 多个点。
这时想到,能 \(2\) 进制拆分,不就能 \(3\) 进制拆分吗?
进制越大,点数越小,边数越大。
换成 \(3\) 进制后可以通过极限数据,但大部分还是不能通过。
这时可以想到枚举进制,取出满足条件的构造就行。
这样已经有 \(80 \%\) 的正确率了。
部分数据会出现边数只超过限制几条的情况,考虑优化。
如果一个点的入度为 \(1\),这个点就是废物,
因为我们可以把它的前驱连向它的所有后继,这样可以省下 \(1\) 点 \(1\) 边。
跑完数位 dp 后再 dfs 一遍进行入度优化,优化完后可以跑过所有点。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 117;
int base, in[N];
int len, m, a[N], f[N][2], cnt, tot;
vector <pair <int, int>> E[N];
vector <pair <int, int>> e[N];
bool vis[N];
int dfs(int x, bool fp) { // 类似数位 dp 的记搜
if (x == 0) return 114; // 没了, 返回终点
if (f[x][fp]) return f[x][fp]; // 建过点了, 返回
int res = ++ cnt, fpmax = fp ? a[x] : base - 1;
for (int i = 0; i <= fpmax; i ++)
E[res].push_back({dfs(x - 1, fp && (i == fpmax)), i * pow(base, x - 1)}); // 连边
f[x][fp] = res; // 记忆化
return res;
}
void dfs2(int x) { // 入度优化
if (vis[x]) return ;
vis[x] = 1;
for (auto [y, z] : E[x]) {
dfs2(y);
if (in[y] == 1 && y != 114) { // 入度为 1 注意不能把终点优化了
for (auto [v, w] : E[y])
e[x].push_back({v, w + z});
E[y].clear();
tot --;
} else e[x].push_back({y, z});
}
E[x] = e[x];
}
bool check(int n) {
for (int i = 0; i < N; i ++)
E[i].clear(), e[i].clear();
memset(f, 0, sizeof(f));
memset(in, 0, sizeof(in));
memset(vis, 0, sizeof(vis));
cnt = m = len = 0;
while (n) {
a[++ len] = n % base;
n /= base;
}
dfs(len, 1); // dp
for (int i = 1; i <= cnt; i ++)
for (auto y : E[i]) in[y.first] ++;
tot = cnt;
dfs2(1); // 优化
for (int i = 1; i <= 114; i ++)
m += E[i].size();
return tot + 1 <= 18 && m <= 45;
}
int main() {
freopen("road.in", "r", stdin);
freopen("road.out", "w", stdout);
int n;
cin >> n;
for (base = 2; base <= 30; base ++) // 枚举进制
if (check(n)) break; // 满足条件就输出
cout << tot + 1 << " " << m << "\n";
for (int i = 1; i <= 114; i ++)
for (auto y : E[i]) {
cout << i << " " << y.first << " " << y.second << "\n";
}
return 0;
}
本文来自博客园,作者:maniubi,转载请注明原文链接:https://www.cnblogs.com/maniubi/p/18526174,orz