1 条题解

  • 0
    @ 2025-4-10 14:01:50

    题意分析

    农夫罗恩要在宽 (W) 、长 (L) 的矩形区域建私人滑雪区,每个土地方格有海拔高度 (H) 。

    奶牛滑雪规则为:可水平或垂直在相邻方格滑行,从高到低单向滑,相同高度可双向滑。

    滑雪缆车可在任意两方格间建,双向且可交叉,为使奶牛能在任意方格间通行,要找最少缆车数量。输入为滑雪区宽 (W) 、长 (L) 以及每个方格的高度。

    解题思路

    1.将每个方格看作图的节点,若满足滑雪条件(高度关系允许滑行),则在对应节点间建边。

    2.使用 Tarjan 算法找出图中的强连通分量,每个强连通分量可看作一个“连通块”,在块内奶牛无需缆车可自由通行。

    3.对于不同强连通分量,统计每个强连通分量的入度 (in) 和出度 (out) 。

    4.最少的滑雪缆车数量等于入度为 0 的强连通分量数量和出度为 0 的强连通分量数量中的较大值。若只有一个强连通分量((id = 1)),则不需要缆车,输出 0 。

    分析

    1.时间复杂度:构建图的时间复杂度为 (O(n × m)) ((n) 为行数,(m) 为列数),Tarjan 算法的时间复杂度为 (O(V + E)) ,其中 (V) 是节点数((V = n × m) ),(E) 是边数((E) 最大为 (4 × n × m) ),统计入度和出度的时间复杂度为 (O(E)) 。所以总的时间复杂度为 (O(n × m)) 。

    2.空间复杂度:使用了邻接表存储图结构((e) 数组、(head) 数组)、记录强连通分量信息的数组((dfn) 、(low) 、(co) )、入度和出度数组((in) 、(out) )等,空间复杂度为 (O(n × m)) 。

    实现步骤

    1.初始化:清空栈,初始化各种数组((in) 、(out) 、(co) 、(head) 、(dfn) 、(low) ),设置计数器 (cnt) 、强连通分量编号 (id) 、时间戳 (tot) 为 0 。

    2.输入处理:读取滑雪区的宽 (W) 、长 (L) 以及每个方格的高度。

    3.图的构建:遍历每个方格,根据滑雪规则在满足条件的相邻方格对应节点间建边(调用 (add) 函数)。

    4.强连通分量求解:对每个未访问的节点调用 Tarjan 算法,找出强连通分量并标记((co) 数组记录每个节点所属的强连通分量)。

    5.统计入度和出度:遍历图的边,若边连接的两个节点属于不同强连通分量,则更新对应强连通分量的入度和出度。

    6.计算结果:统计入度为 0 和出度为 0 的强连通分量数量 (x) 和 (y) ,输出 (max(x, y)) ;若只有一个强连通分量((id = 1)),输出 0 。

    代码实现

    #include <cstdio>
    #include<iostream>
    #include <cstring>
    #include <algorithm>
    #include <stack>
    using namespace std;
    #define ll long long
    const int maxm=1000;
    const int maxn=1e6;
    stack<int>q;int mp[maxm][maxm];
    int cnt,n,m,head[maxn],dfn[maxn],low[maxn],co[maxn],tot,id,out[maxn],in[maxn];
    struct Edge
    {
    	int to,next;
    }e[maxn*10];
    void add(int u,int v)
    {
    	e[cnt].to=v;
    	e[cnt].next=head[u];
    	head[u]=cnt++;
    }
    void init()
    {
    	memset(in,0,sizeof(in));
    	memset(out,0,sizeof(out));
    	memset(co,0,sizeof(co));
    	memset(head,-1,sizeof(head));
    	memset(dfn,0,sizeof(dfn));
    	memset(low,0,sizeof(low));
    	while(!q.empty()) q.pop();
    	cnt=0;id=0,tot=0;
    }
    void tarjan(int u)
    {
    	q.push(u);
    	dfn[u]=low[u]=++tot;
    	for(int i=head[u];i!=-1;i=e[i].next)
    	{
    		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(low[u]==dfn[u])
    	{
    		id++;
    		while(q.top()!=u)
    		{
    			int x=q.top();
    			q.pop();
    			co[x]=id;
    		}
    		co[u]=id;
    		q.pop();
    	}
    }
    int js(int i,int j)
    {
    	return (i-1)*m+j;
    }
    int main()
    {
    	while(scanf("%d %d",&m,&n)!=EOF)
    	{
    		init();
    		for(int i=1;i<=n;i++)
    		{
    			for(int j=1;j<=m;j++)
    			{
    				scanf("%d",&mp[i][j]);
    			}
    		}
    		for(int i=1;i<=n;i++)
    		{
    			for(int j=1;j<=m;j++)
    			{
    				//printf("js(%d %d)=%d js(%d %d)=%d js(%d %d)=%d js(%d %d)=%d\n",i-1,j,js(i-1,j),i+1,j,js(i+1,j),i,j-1,js(i,j-1),i,j+1,js(i,j+1));
    				if(i>1&&mp[i][j]>=mp[i-1][j]) add(js(i,j),js(i-1,j));
    				if(i<n&&mp[i][j]>=mp[i+1][j]) add(js(i,j),js(i+1,j));
    				if(j>1&&mp[i][j]>=mp[i][j-1]) add(js(i,j),js(i,j-1));
    				if(j<m&&mp[i][j]>=mp[i][j+1]) add(js(i,j),js(i,j+1));
    			}
    		}
    		int ed=n*m;
    		for(int i=1;i<=ed;i++)
    		{
    			if(dfn[i]==0)
    			{
    				tarjan(i);
    			}
    		}
    		
    		if(id==1) printf("0\n");
    		else
    		{
    			for(int i=1;i<=ed;i++)
    			{
    				for(int j=head[i];j!=-1;j=e[j].next)
    				{
    					int v=e[j].to;
    					if(co[i]!=co[v])
    					{
    						//printf("i=%d j=%d u=%d v=%d\n",i,v,co[i],co[v]);
    						out[co[i]]++;
    						in[co[v]]++;
    					}
    				}
    			}
    			int x=0,y=0;
    			for(int i=1;i<=id;i++)
    			{
    				//printf("out[%d]=%d in[%d]=%d\n",i,out[i],i,in[i]);
    				if(out[i]==0) x++;
    				if(in[i]==0) y++;
    			}
    			printf("%d\n",max(x,y));
    		}
    	}
    }
    

    代码说明

    1.头文件和命名空间:包含了 <cstdio> 用于输入输出操作,<iostream> 用于输入输出流,<cstring> 用于字符串和数组初始化操作,<algorithm> 用于算法相关操作(如 min 函数),<stack> 用于栈操作。使用 using namespace std; 使代码可以直接使用标准命名空间中的函数和类型。

    2.宏定义和常量定义#define ll long long 定义 lllong long 类型。const int maxm = 1000;const int maxn = 1e6; 定义了一些数组大小的上限。

    3.变量定义mp[maxm][maxm] 存储每个方格的高度;q 是用于 Tarjan 算法的栈;cnt 用于边的计数,nm 分别表示滑雪区的长和宽,head[maxn] 是邻接表表头数组,dfn[maxn]low[maxn] 用于 Tarjan 算法记录时间戳和最小时间戳,co[maxn] 记录每个节点所属的强连通分量编号,tot 是时间戳,id 是强连通分量编号,out[maxn]in[maxn] 分别记录强连通分量的出度和入度。

    4.结构体定义Edge 结构体定义了边的信息,包括目标节点 to 和下一条边的编号 next

    5.函数定义: - add 函数用于向邻接表中添加边。 - init 函数用于初始化各种变量和数组。 - tarjan 函数实现了 Tarjan 算法,用于找出强连通分量。 - js 函数将二维坐标转换为一维节点编号。

    6.主函数: - 通过 while(scanf("%d %d", &m, &n) != EOF) 循环读取输入数据。 - 每次输入前调用 init 函数初始化。 - 读取每个方格的高度并构建图。 - 对每个未访问节点调用 tarjan 函数找出强连通分量。 - 根据强连通分量信息统计入度和出度。 - 根据入度和出度情况输出最少滑雪缆车数量。

    • 1

    信息

    ID
    1376
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者