题目大意
给出一个大小为N × M (1 ≤ N, M ≤ 300)的矩形,矩形的每个位置有一个高度 h (1 ≤ h ≤ 1,000,000)。在相邻的格点间,你可以上下或者左右移动。但是你只能从高的格点走向不比它高的格点。相同高度的格点间两个方向都能移动。
现在,你需要计算出至少需要添加多少条双向边才能使到矩阵任意两点之间都可以互相。
分析
根据规则建图后缩点,统计各个点的入度和出度。
再分别统计入度和出度为零的点的数量,输出较大的那个。
code
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
using namespace std;
struct arr{
int x,y,w,next;
}edge[1000000];
int ls[100000];
int edge_m=0;
int n,m;
void add(int x,int y)
{
edge[++edge_m]=(arr){x,y,1,ls[x]},ls[x]=edge_m;
return;
}
int v[100000];
int dfn[100000];
int low[100000];
int scc[100000];
int ans=0;
int num=0;
stack<int> sta;
void tarjan(int x)
{
sta.push(x);
num++;
dfn[x]=low[x]=num;
v[x]=1;
for (int i=ls[x];i;i=edge[i].next)
{
int y=edge[i].y;
if (dfn[y]==0)
{
tarjan(y);
low[x]=min(low[x],low[y]);
}
else
if (v[y]) low[x]=min(dfn[y],low[x]);
}
if (dfn[x]==low[x])
{
ans++;
while ((sta.top()!=x)&&(!sta.empty()))
{
int y=sta.top();
v[y]=0;
scc[y]=ans;
sta.pop();
}
int y=sta.top();
v[y]=0;
scc[y]=ans;
sta.pop();
}
}
int a[400][400];
int sx(int x,int y)
{
return (x-1)*m+y;
}
int chu[100000];
int ru[100000];
int main()
{
int dx[10],dy[10];
dx[1]=1;dx[2]=-1;dx[3]=0;dx[4]=0;
dy[1]=0;dy[2]=0;dy[3]=-1;dy[4]=1;
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
for (int k=1;k<=4;k++)
{
if ((i+dx[k]>n)||(i+dx[k]<1)||(j+dy[k]>m)||(j+dy[k]<1))
continue;
if (a[i][j]>=a[i+dx[k]][j+dy[k]])
add(sx(i,j),sx(i+dx[k],j+dy[k]));
}
for (int i=1;i<=n*m;i++)
if (scc[i]==0)
tarjan(i);
for (int i=1;i<=edge_m;i++)
if (scc[edge[i].x]!=scc[edge[i].y])
{
chu[scc[edge[i].x]]++;
ru[scc[edge[i].y]]++;
}
int anss=0;
for (int i=1;i<=ans;i++)
if (chu[i]==0)
anss++;
int ansss=0;
for (int i=1;i<=ans;i++)
if (ru[i]==0)
ansss++;
anss=max(anss,ansss);
if (ans==1)
anss=0;
printf("%d",anss);
fclose(stdin);
fclose(stdout);
}