1 条题解
-
0
自动搬运
来自洛谷,原作者为

pldzy
就像我一直听香夭从未沾湿眼角。搬运于
2025-08-24 21:36:20,当前版本为作者最后更新于2021-08-16 21:50:18,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
思路
我们要先找到所有的升级序列,找出所有的包含与被包含的关系,
然后找到一条最长链,满足题目要求。
-
如何找到所有的包含关系?
遍历每一对盒子,对于二者,我们用 dfs 扫一遍,如果有一个盒子在一个节点输出了串,而另一个没有,那么这两者就不存在包含关系。
反之,两者就存在包含关系。我们就可以建一条有向边,从包含者指向被包含者。
-
为什么求最长链?
易得,在一个强连通分量中,肯定是存在一条链的。
所以,我们只需要把一个个强连通分量缩点,然后找到一条把超级点(强连通分量)串起来的最长的链即可。
最终答案,就是最长链的长度。
-
怎么找最长链?
还是用 dfs,我们分别从每一个节点开始遍历,以当前节点当作当前链的链顶,
然后用前向星搜下去,每次返回以该点作为链顶时最长链的长度。
代码实现
-
初始化:
有两个细节一定要注意:
-
一定要开
long long; -
用前向星的话存边数组至少开到 400。
-
-
输入:
我们开个结构体存一下输入条件。
注意输入的点的编号要加一(题目编号从 0 开始)。
void input () { scanf ("%lld", &s); for (int i = 1; i <= s; i++) { scanf ("%lld %lld", &n, &m); for (int j = 1; j <= m; j++) { int t; scanf ("%lld", &t); t++; a[i].out[t] = 1; } for (int l = 1; l <= n; l++) { int aa, bb; scanf ("%lld %lld", &aa, &bb); aa++, bb++; a[i].lin[l][0] = aa, a[i].lin[l][1] = bb; } } }-
建边:
每次对于任意两个宝盒,我们同时从 1 开始出发(题目已知条件),如果他们之间存在包含关系,
证明他们两者间必定有完全相同的 01 串。
所以,如果当我们访问到一个节点时,一个宝盒中的是输出元,而另一个不是,
就证明这两个间不存在包含关系。
反之,即存在,我们就可以在他们之间建一条有向边,指向他们之间的包含关系。
记得要用数组储存一下每一条边的起点和终点,方便后面缩点,重新建图。
void find (int x, int y, int nx, int ny) { if (a[x].out[nx] == 1 and a[y].out[ny] == 0) { flag = 1; return; } if (vis[nx][ny]) return; vis[nx][ny] = 1; find (x, y, a[x].lin[nx][0], a[y].lin[ny][0]); find (x, y, a[x].lin[nx][1], a[y].lin[ny][1]); } void build () { for (int i = 1; i <= s; i++) { for (int j = 1; j <= s; j++) { if (i == j) continue; flag = 0, memset (vis, 0, sizeof vis); find (i, j, 1, 1); if (!flag) add (i, j), u[cnt] = i, v[cnt] = j; } } }-
缩点:
按照之前思路所说的我们要缩点,以找最长链。
这里缩点就不多说了,具体可以见我的博客缩点(算法笔记)。
void tarjan (int u) { dfn[u] = low[u] = ++tmp; st[++top] = u; for (int i = hd[u]; i; i = e[i].nxt) { int v = e[i].to; if (!dfn[v]) { tarjan (v); low[u] = min (low[u], low[v]); } else if (!co[v]) low[u] = min (low[u], dfn[v]); } if (dfn[u] == low[u]) { co[u] = ++col; siz[col] = 1; while (st[top] != u) { co[st[top]] = col; siz[col]++; top--; } top--; } } void rebuild () { for (int i = 1; i <= s; i++) if (!dfn[i]) tarjan (i); int recoc = cnt; cnt = 0; memset (hd, 0, sizeof hd); memset (e, 0, sizeof e); for (int i = 1; i <= recoc; i++) { if (co[u[i]] == co[v[i]]) continue; add (co[u[i]], co[v[i]]); } }-
输出答案:
这时候我们就可以直接记忆化搜索最长链了,最后边搜边记录最长链长度即可。
int get (int u) { if (ans[u]) return ans[u]; ans[u] = siz[u]; for (int i = hd[u]; i; i = e[i].nxt) { int v = e[i].to; ans[u] = max (ans[u], get (v) + siz[u]); } return ans[u]; } void output () { int anss = 0; for (int i = 1; i <= col; i++) if (!ans[i]) anss = max (anss, get (i)); printf ("%lld\n", anss); }完整代码
#include<bits/stdc++.h> using namespace std; #define int long long int s, n, m; const int maxn = 205; int cnt, hd[maxn]; struct node{ int to, nxt; }e[305]; struct node2{ int lin[maxn][5], out[maxn]; }a[maxn]; int dfn[maxn], low[maxn], siz[maxn], co[maxn], st[maxn]; int tmp, top, col; int flag, vis[maxn][maxn]; int u[305], v[305], ans[maxn]; void add (int u, int v) { e[++cnt].to = v; e[cnt].nxt = hd[u]; hd[u] = cnt; } void input () { scanf ("%lld", &s); for (int i = 1; i <= s; i++) { scanf ("%lld %lld", &n, &m); for (int j = 1; j <= m; j++) { int t; scanf ("%lld", &t); t++; a[i].out[t] = 1; } for (int l = 1; l <= n; l++) { int aa, bb; scanf ("%lld %lld", &aa, &bb); aa++, bb++; a[i].lin[l][0] = aa, a[i].lin[l][1] = bb; } } } void find (int x, int y, int nx, int ny) { if (a[x].out[nx] == 1 and a[y].out[ny] == 0) { flag = 1; return; } if (vis[nx][ny]) return; vis[nx][ny] = 1; find (x, y, a[x].lin[nx][0], a[y].lin[ny][0]); find (x, y, a[x].lin[nx][1], a[y].lin[ny][1]); } void build () { for (int i = 1; i <= s; i++) { for (int j = 1; j <= s; j++) { if (i == j) continue; flag = 0, memset (vis, 0, sizeof vis); find (i, j, 1, 1); if (!flag) add (i, j), u[cnt] = i, v[cnt] = j; } } } void tarjan (int u) { dfn[u] = low[u] = ++tmp; st[++top] = u; for (int i = hd[u]; i; i = e[i].nxt) { int v = e[i].to; if (!dfn[v]) { tarjan (v); low[u] = min (low[u], low[v]); } else if (!co[v]) low[u] = min (low[u], dfn[v]); } if (dfn[u] == low[u]) { co[u] = ++col; siz[col] = 1; while (st[top] != u) { co[st[top]] = col; siz[col]++; top--; } top--; } } void rebuild () { for (int i = 1; i <= s; i++) if (!dfn[i]) tarjan (i); int recoc = cnt; cnt = 0; memset (hd, 0, sizeof hd); memset (e, 0, sizeof e); for (int i = 1; i <= recoc; i++) { if (co[u[i]] == co[v[i]]) continue; add (co[u[i]], co[v[i]]); } } int get (int u) { if (ans[u]) return ans[u]; ans[u] = siz[u]; for (int i = hd[u]; i; i = e[i].nxt) { int v = e[i].to; ans[u] = max (ans[u], get (v) + siz[u]); } return ans[u]; } void output () { int anss = 0; for (int i = 1; i <= col; i++) if (!ans[i]) anss = max (anss, get (i)); printf ("%lld\n", anss); } signed main () { input (); build (); rebuild (); output (); return 0; }如有问题,求大佬斧正,感谢。
-
- 1
信息
- ID
- 1315
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者