1336. 【CCF2017第四期NOI教师培训测试试题】蒸发学水(100%数据) (Standard IO)

文章讲述了如何使用并查集优化解决TerryHu蒸发学水问题,计算不同时刻后的联通块数量,提供了两种代码实现,对比了运行效率。
摘要由CSDN通过智能技术生成

时间限制: 1000 ms  空间限制: 262144 KB  具体限制  

题目描述

众所周知,TerryHu 是一位大佬,他平时最喜欢做的事就是蒸发学水。
机房的位置一共有n 行m 列,一开始每个位置都有一滴学水,TerryHu 决定在每一个时刻选择
一滴学水进行蒸发,直到机房里不再存在学水。
TerryHu 想知道在每个时刻之后,机房里剩下的学水构成了几个联通块

输入

第一行包含2 个正整数n,m。
之后n 行每行包含m 个正整数Ai;j,表示第i 行第j 列的学水在时刻Ai;j 被蒸发,保证{A}
构成了一个n *m 的排列。

输出

共n * m 行每行包含1 个整数ansi,时刻i 之后剩下的学水构成的联通块的数量。

样例输入
2 2

1 3

4 2
样例输出
1

2

1

0
数据范围限制

数据范围:



对于60%
的数据:n,m<= 50;



对于100%
的数据:n,m<= 1000。

首先,我们可以用宽搜(广搜)去解

#include<iostream>//蒸发学水 
using namespace std;
int n,m,ans,f,mp[1001][1001],a[1000001][2],d[1000001][2];
int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
void go(int x,int y)
{
	ans++;
	f++;
	mp[x][y]=2;
	int t=0,w=1,xx,yy;
	d[1][0]=x;
	d[1][1]=y;
	do
	{
		t++;
		for(int i=0;i<=3;i++)
		{
			xx=d[t][0]+dx[i];
			yy=d[t][1]+dy[i];
			if(xx>0&&xx<=n&&yy>0&&yy<=m&&mp[xx][yy]==0)
			{
				w++;
				d[w][0]=xx;
				d[w][1]=yy;
				mp[xx][yy]=2;
				f++;
			}
		}
	}while(t<w);
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	{
		cin>>f;
		a[f][0]=i;
		a[f][1]=j;
	}
	for(int k=1;k<=n*m;k++)
	{
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=m;j++)
			{
				if(mp[i][j]==2)
				 mp[i][j]=0;
			}
		}
		mp[a[k][0]][a[k][1]]=1;
		ans=0;
		f=k;
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=m;j++)
			{
				if(f==n*m)
				{
					break;
				}
				else if(mp[i][j]==0)
				{
					go(i,j);
				}
			}
			if(f==n*m)
				break;
		}
		cout<<ans<<endl;
	}
}

但我们发现,只能的60分,但我也没办法

才怪

并查集啊啊啊啊啊啊啊啊啊啊啊啊啊啊

#include<bits/stdc++.h>
using namespace std;
int f1[1000110],f2[1000110],fa[1000110],ans[1000110],a[1010][1010],tot=0;//f1记录行  f2记录列 fa记录祖先 ans记录当前位子的连通块的情况 
bool b[1010][1010];//标记当前行列的水的状态 0是已经蒸发  1是没蒸发 
int fx[5]={0,1,0,-1,0};
int fy[5]={0,0,1,0,-1};
int find(int i)//并查集 的查找 
{
	if(fa[i]==i)
	return i;
	else
	{
		fa[i]=find(fa[i]);
		return fa[i];
	}
}
int unionn(int x,int y)//合并 
{
	int xfa=find(x);
	int yfa=find(y);
	fa[xfa]=yfa;
}
int main()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			scanf("%d",&a[i][j]);
			f1[a[i][j]]=i;
			f2[a[i][j]]=j;
		}
	for(int i=n*m;i>=1;i--)
	{
		int x=f1[i];
		int y=f2[i];
		b[x][y]=1;
		fa[x*m+y]=x*m+y;//并查集的初始化祖先 
		tot++;
		for(int j=1;j<=4;j++)
		{
			int xx=x+fx[j];
			int yy=y+fy[j];
			if(xx>=1 and xx<=n and yy>=1 and yy<=m and b[xx][yy]==1 and find(xx*m+yy)!=find(x*m+y))
			{
				unionn(x*m+y,xx*m+yy);
				tot--;
			}
		}
		ans[i]=tot;
	}
	for(int i=2;i<=n*m+1;i++) printf("%d\n",ans[i]); 
	return 0;
}

387ms  20mb

当然还有网传方法

#include <iostream>

using namespace std;

const int MAXSIZE = 1000*1000+1;
int uset[MAXSIZE];
int answer[MAXSIZE];
pair<short,short> valueToIndex[MAXSIZE];
const int dx[]={-1,0,0,1},dy[]={0,-1,1,0};

//void makeSet(int size) {
//   for(int i = 1;i <= size;i++)  uset[i] = -MAXSIZE;
//}

int find(int x) {
    int p = x, t;
    while (uset[p] > 0) p = uset[p];
    while (x != p ) {
        t = uset[x];
        uset[x] = p;
        x = t;
    }
    return x;
}

void unionSet(int x, int y) {
    if ((x = find(x)) == (y = find(y))) return;
    if (uset[x] < uset[y]) {
        uset[x] += uset[y];
        uset[y] = x;
    } else {
        uset[y] += uset[x];
        uset[x] = y;
    }
}

void unionSetRoot(int x,int y)
{
    if (uset[x] < uset[y]) {
        uset[x] += uset[y];
        uset[y] = x;
    } else {
        uset[y] += uset[x];
        uset[x] = y;
    }
}

int raw,col;
int main()
{

   scanf("%d%d",&raw,&col);
   int size = raw * col;
   //makeSet(size);

   for(short i=1;i<=raw;i++)
    for(short j=1;j<=col;j++)
    {
        int value;
        scanf("%d",&value);
        valueToIndex[value] = make_pair(i,j);
    }

    for(int evaWater=size-1;evaWater>=1;evaWater--)
    {
        int lastEvaWater = evaWater+1;
        int x = valueToIndex[lastEvaWater].first;
        int y = valueToIndex[lastEvaWater].second;
        int lastValueIndex = (x-1)*col +y;
        uset[lastValueIndex] = -1;
        answer[evaWater] = answer[lastEvaWater]+1;

        for(int dir=0;dir<4;dir++)
        {
            int tx=x+dx[dir],ty=y+dy[dir];

            if(tx >=1&& tx <=raw && ty>=1 && ty<= col )
            {
                int los =(tx-1)*col+ty;

                if( uset[los] !=0 )
                {
                    int key = find(los);
                    int last = find(lastValueIndex);
                    if(last != key)
                    {
                        unionSetRoot(last,key);
                        answer[evaWater]--;
                    }

                }

            }
        }


    }


  for (int i=1;i<=size;i++)
        printf("%d\n",answer[i]);

   return 0;
}

才343ms!!!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值