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;
}