大扫除

Description

现在要打扫hl的牧圈。已经很多年没打扫了。所以这次的计划是用河水来冲。
牧圈排成整齐的格子,每相邻的两个之间都有门。要想让水进去,就必须打开这些门。这不是件容易的事情。因为有些圈里土堆得很高。因此打开门就很费劲。为了使花的力气最小,总是把门推向土低的一边。你的任务是计算最少得费多少劲。我们用土的厚度来描述这个值。

Input

第一行是宽度w和高度h,其中3<=w,h<=40。以下h行数据,描述了土的高度,也就是我们所浪费体力的度量。数据的范围在1到100之间。

Output

你得到的结果。所有的格子都必须进水。水是从左上角的格子进去的。

Sample Input

4 3
3 5 2 1
7 3 4 8
1 6 5 7

Sample Output

26

 

【分析】

        这题第一眼感觉是floodfill,然后就想到BFS。但是再仔细一分析,是最小生成树。这是个很不错的模型,要仔细分析才能想到。想到后就简单了。相邻两格连边,权值为较小的那格的权值。然后跑一次Kruskal或者Prim就出来答案了。

 

【代码】

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<ctime>
#include<iostream>
#include<algorithm>
using namespace std;
int N,M,Map[100][100],g[100][100],tot,totn;
int x[20000],y[20000],z[20000];
int father[20000];
int dx[4]={1,0,0,-1},dy[4]={0,1,-1,0};
void _init()
{
	scanf("%d%d",&M,&N);
	for(int i=1;i<=N;i++)
		for(int j=1;j<=M;j++)
		{
			g[i][j]=++totn;
		    scanf("%d",&Map[i][j]);
		}
}
int _getfather(int x)
{
	if(x!=father[x]) father[x]=_getfather(father[x]);
	return father[x];
}
void _qst_edge(int l,int r)
{
	int i=l,j=r,mz=z[(i+j)>>1];
	while(i<=j)
	{
		while(z[i]<mz) i++;
		while(z[j]>mz) j--;
		if(i<=j)
		{
			swap(x[i],x[j]);swap(y[i],y[j]);swap(z[i],z[j]);
			i++;j--;
		}
	}
	if(l<j) _qst_edge(l,j);
	if(i<r) _qst_edge(i,r);
}
void _Kruskal()
{
	int fx,fy,k,ans,cnt;
	k=ans=cnt=0;
	while(cnt<totn-1)
	{
		k++;
		fx=_getfather(x[k]);
		fy=_getfather(y[k]);
		if(fx!=fy)
		{
			father[fx]=fy;
			cnt++;
			ans+=z[k];
		}
	}
	printf("%d\n",ans);
}
void _solve()
{
	for(int i=1;i<=N;i++)
		for(int j=1;j<=M;j++)
			for(int k=1;k<=3;k++)
				if(1<=i+dx[k]&&i+dx[k]<=N)
					if(1<=j+dy[k]&&j+dy[k]<=M)
					{
						tot++;
						x[tot]=g[i][j];
						y[tot]=g[i+dx[k]][j+dy[k]];
						z[tot]=min(Map[i][j],Map[i+dx[k]][j+dy[k]]);
						tot++;
						x[tot]=y[tot-1];
						y[tot]=x[tot-1];
						z[tot]=z[tot-1];
					}
	for(int i=1;i<=totn;i++)
		father[i]=i;
    _qst_edge(1,tot);
	_Kruskal();
}
int main()
{
	_init();
	_solve();
	return 0;
}


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值