POJ2375 暴力建图+tarjan缩点

POJ2375

题意:

给一个n*m,n,m<=500的格子滑雪场,每个格子有个海拔,可以上下左右滑,

海拔高的可以滑到海拔低的,海拔相同的可以互相滑

现在要建若干电梯(选两个格子使其可以互相到达,无海拔限制)

使得从任意一个点出发都可以经过整个滑雪场

问最少需要建多少个电梯

思路:

其实就是问最少加多少条边使得整个图变成强连通图

对于缩点后的DAG

入度为0 的点的数目 ,出度为0的点的数目之间的较大值就是答案了

(画几个图体会体会)

如果一开始已经是强连通就输出0

建图直接暴力!

坑点:

g++ re,c++wa,数组开800才过

282ms

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h> 
using namespace std;
const int maxn=805;
const int maxe=640005;
int low[maxn*maxn];
int dfn[maxn*maxn];
bool ins[maxn*maxn];
int sk[maxn*maxn];
int poi=0,idx=0,cnt=0;
int num=0;
int head[maxn*maxn];
int to[maxe*2];
int nxt[maxe*2];

//bool vis[505][505];
int mp[605][605];
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
int n,m;
int in[maxn*maxn];//入度 
int ou[maxn*maxn];//出度 
int scc[maxn*maxn];
void add(int u,int v)
{
	cnt++;nxt[cnt]=head[u];
	head[u]=cnt; to[cnt]=v;
}
void dfs(int x,int y)
{
    // if(vis[x][y]==1)return ;
    // vis[x][y]=1;
     for(int i=0;i<=3;i++)
     {
         int tx=x+dx[i];
         int ty=y+dy[i];
         if(tx<1||tx>n||ty<1||ty>m)continue;
      //   printf("%d %d %d %d\n",x,y,tx,ty);
         if(mp[tx][ty]<=mp[x][y])
             add((x-1)*m+y,(tx-1)*m+ty);//printf("???");//m:lie
        // if(mp[tx][ty]==mp[x][y])
        //     add((x-1)*m+y,(tx-1)*m+ty),add((tx-1)*m+ty,(x-1)*m+y);                    
        //if(vis[tx][ty]==0&&mp[tx][ty]<=mp[x][y])
        //    dfs(tx,ty);
     }
}

void tarjan(int u)
{
	dfn[u]=low[u]=++idx;
	ins[u]=1;
	sk[poi++]=u;
	for(int i=head[u];i!=-1;i=nxt[i])
	{
		int v=to[i];
		if(!dfn[v])
		{
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(ins[v]) low[u]=min(low[u],dfn[v]);
	}
	if(low[u]==dfn[u])
	{
		num++;
		int t;
		do{
			t=sk[--poi];
			scc[t]=num;
			ins[t]=0;
		}
		while(t!=u);
	}
}

int main()
{
	scanf("%d %d",&m,&n);
	
		memset(ins,0,sizeof(ins));
		memset(dfn,0,sizeof(dfn));
		memset(low,0,sizeof(low));
		memset(head,-1,sizeof(head));
		memset(ou,0,sizeof(ou));
		memset(in,0,sizeof(in));
//        memset(vis,0,sizeof(vis));
		num=poi=cnt=idx=0;
	    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++)
                dfs(i,j);
        /*
        for(int u=1;u<=n*m;u++)
        {
            printf("dian u:%d :",u);
            for(int i=head[u];i!=-1;i=nxt[i])
                printf(" %d",to[i]);
            puts("");
        }*/
        
		for(int i=1;i<=n*m;i++)
		{
			if(!dfn[i])
				tarjan(i);
		}
		int ans=0;
		for(int u=1;u<=n*m;u++)
		{
			for(int i=head[u];i!=-1;i=nxt[i])
			{
				int v=to[i];
				if(scc[u]!=scc[v])
				{
					ou[scc[u]]++;
					in[scc[v]]++;
				}
			}
		} 
	//	printf("num:%d\n",num);
		if(num==1)
		    printf("0\n");
        else
        {
      		int ans1=0;int ans2=0;
	    	for(int i=1;i<=num;i++)
	     	{
                        if(in[i]==0)ans1++;
		    	if(ou[i]==0)ans2++;
	      	}
	       	printf("%d\n",max(ans1,ans2));
        }


		
} 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值