1 条题解

  • 0
    @ 2025-8-24 21:36:20

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar pldzy
    就像我一直听香夭从未沾湿眼角。

    搬运于2025-08-24 21:36:20,当前版本为作者最后更新于2021-08-16 21:50:18,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    思路

    我们要先找到所有的升级序列,找出所有的包含与被包含的关系,

    然后找到一条最长链,满足题目要求。

    1. 如何找到所有的包含关系?

      遍历每一对盒子,对于二者,我们用 dfs 扫一遍,如果有一个盒子在一个节点输出了串,而另一个没有,那么这两者就不存在包含关系。

      反之,两者就存在包含关系。我们就可以建一条有向边,从包含者指向被包含者。

    2. 为什么求最长链?

      易得,在一个强连通分量中,肯定是存在一条链的。

      所以,我们只需要把一个个强连通分量缩点,然后找到一条把超级点(强连通分量)串起来的最长的链即可。

      最终答案,就是最长链的长度。

    3. 怎么找最长链?

      还是用 dfs,我们分别从每一个节点开始遍历,以当前节点当作当前链的链顶,

      然后用前向星搜下去,每次返回以该点作为链顶时最长链的长度。

    代码实现

    1. 初始化:

      有两个细节一定要注意:

      1. 一定要开 long long

      2. 用前向星的话存边数组至少开到 400。

    2. 输入:

      我们开个结构体存一下输入条件。

      注意输入的点的编号要加一(题目编号从 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. 建边:

      每次对于任意两个宝盒,我们同时从 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;
    		}	
    	}
    }
    
    1. 缩点:

      按照之前思路所说的我们要缩点,以找最长链。

      这里缩点就不多说了,具体可以见我的博客缩点(算法笔记)

    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]]);
    	}
    }
    
    1. 输出答案:

      这时候我们就可以直接记忆化搜索最长链了,最后边搜边记录最长链长度即可。

    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
    上传者