P2124 奶牛美容

题目描述

输入输出格式

输入格式:

输出格式:

输入输出样例

输入样例#1: 
6 16
................
..XXXX....XXX...
...XXXX....XX...
.XXXX......XXX..
........XXXXX...
..XXX....XXX....
输出样例#1: 
4

Solution:

  本题一眼想到搜索(考试时打了半天,没调对~~手动滑稽~~)。事后讨论,发现神佬的思路有可行性也有一些错误,于是从中启发,再结合题解,便改出了类$floyd$的算法。

  首先,用一次$dfs$处理出$3$个连通块并标记,实现比较简单。

  再就是骚操作了,处理出每个$s[i][j]==X$的点所在连通块到其它所有点的最小曼哈顿距离$dis[k][i][j]$(表示第$k$个连通块到$[i,j]$点的最小曼哈顿距离)。

  然后对于每个是连通块上的点(即$s[i][j]==X$的点),去更新这个点所在连通块与其它连通块的最短距离($f[i][j]$表示第$i$个连通块与第$j$个连通块的最短距离),很显然的状态转移方程为:$f[p][q]=min(f[p][q],dis[q][i][j]),;s[i][j]in p$,(记得$f[p][q]=f[q][p]$)。

  然后就处理出了$3$个连通块两两之间的最短距离,则$ans$初值是$3$条边中$2$条搭配的最小值(因为要使$3$个连通块连通只要连$2$条边),至于为什么是初值赋值为这个最小值呢?

  那是因为可能存在两两连通块之间共用了点的情况,举个例子:

  $egin{Bmatrix}
 X;.;.;.;X\
 .;;.;.;.;;.\
 .;;.;.;.;;. \
 .;.;X;.;.
end{Bmatrix}$

  此时求出的$3$个连通块之间的最小距离和为$9$则需要加的$X$个数为$9-2=7$(减$2$是很显然的道理,因为我们是以坐标算曼哈顿距离,两个点之间曼哈顿路径上的点数应该等于曼哈顿距离减$1$(有点类似于小学奥数的植树间隔问题),那么三个点之间实际上就是两个两个点之间的点数,显然是减$2$(怎么感觉自己说的有点绕,~手动滑稽,不信自己模拟~)),但这个例子的答案显然因该是$5$。

  很容易发现,上述情况属于三个连通块之间由非连通块上的点搭桥相连的情况(这不还是$floyd$嘛)。于是我们处理出初值$ans$后,还应该枚举中间点,计算三个点之间共用点搭桥的情况的最小值,更新$ans$。

  那么最后答案就是$ans-2$了。时间复杂度$O(n^3)$(这也只是最坏复杂度,基本体现在预处理枚举每个连通块到其它所有点的最小曼哈顿距离上了),但还是很轻易本题就$AC$了(貌似最优解!)。

  本题给我的启发就是:

  1、其实可以将连通块个数再增多,那么就是跑最小生成树处理初值$ans$,同理继续枚举中间点连接各连通块。

  2、其实$nleq 50$的数据用此方法还是很水的,可以增大数据至少到$nleq 200$。

代码:

#include<bits/stdc++.h>
#define il inline
#define ll long long
#define debug printf("%d %s
",__LINE__,__FUNCTION__)
using namespace std;
int n,m,ans=520520520,p[55][55],cnt,f[55][55],tot,dis[4][55][55];
int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
char s[55][55];
bool vis[55][55],lian[4];
il void change(int x,int y,int c){
    if(vis[x][y])return ;
    if(s[x][y]=='X'){vis[x][y]=1;p[x][y]=c;}
    else return;
    for(int i=0;i<4;i++){
        int xx=dx[i]+x,yy=dy[i]+y;
        change(xx,yy,c);
    }
}
il void dfs(int kuai,int x,int y){
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            dis[kuai][i][j]=min(dis[kuai][i][j],abs(i-x)+abs(j-y));
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        if(s[i][j]=='X'&&!vis[i][j])change(i,j,++cnt);
    memset(dis,0x3f,sizeof(dis));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        if(s[i][j]=='X')dfs(p[i][j],i,j);
    memset(f,0x3f,sizeof(f));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        if(s[i][j]=='X'){
            f[p[i][j]][1]=min(f[p[i][j]][1],dis[1][i][j]);
            f[p[i][j]][2]=min(f[p[i][j]][2],dis[2][i][j]);
            f[p[i][j]][3]=min(f[p[i][j]][3],dis[3][i][j]);
            f[1][p[i][j]]=f[p[i][j]][1];
            f[2][p[i][j]]=f[p[i][j]][2];
            f[3][p[i][j]]=f[p[i][j]][3];
    }
    ans=min(f[1][2]+f[2][3],min(f[1][3]+f[1][2],f[1][3]+f[2][3]));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            ans=min(ans,dis[1][i][j]+dis[2][i][j]+dis[3][i][j]);
    cout<<ans-2;
    return 0;
}

 

原文地址:https://www.cnblogs.com/five20/p/8903484.html