题解 Luogu P1514 【引水入城】

有一种神奇的算法叫做floodfill

就是一个n*m的矩阵,a[i][j]为当前高度,我们可以任选一个点倒水,开始bfs,如果要搜的点没有被搜到过,并且高度小于当前的点,我们就把这个点加入队列中

而这道题我们可以用floodfill做

先假设有解,也就是说假设干旱区里的所有城区都能建水利措施。

对于第一行湖泊,显然我们bfs往下floodfill覆盖的最后一行肯定是个区间,那么对于第一行两个点floodfill后覆盖的最后一行的两个区间,如果左边端点floodfill的右边一部分与右边端点floodfill的左边一部分有相交部分,那么这两个floodfill最后一行所覆盖的区间的左端点肯定是相同的,但是我们选择建蓄水厂肯定是选择右边端点,因为右边端点的floodfill能够多覆盖一部分。

所以我们从左往右枚举最后一行的端点,从最后一行的端点先倒着做一遍floodfill,所覆盖的第一行的区间中选择最右边的端点,再正着做一遍floodfill,直接标记floodfill中扫到的最后一行的区间里的端点,再把这个区间的右端点的右边一个点进行倒着做一遍floodfill......就这样循环做下去直到(n,m)被标记

那么怎么判断无解呢?显然,我们把第一行所有端点都做一遍正着的floodfill,也就是说能建蓄水厂的都建起来。再统计最后一行的没被标记的点数,如果有被标记的,就按照无解的输出形式输出,否则就是有解,按照有解的办法解决问题

可能解释有些不清,可以线下找我)

那么就是我们的代码,其中booms函数是正着floodfill,boom函数是倒着floodfill

Code:

#include<iostream>
#include<cstdio>
using namespace std;
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
int n,m,a[10001][10001];
bool vis[10001][10001];
bool viss[10001][10001];
int qx[400001],qy[400001];
int ans;
int boom(int p)
{
    int tail=1,head=1;
    vis[n][p]=1;
    qx[1]=n;
    qy[1]=p;
    while(head<=tail)
    {
        int x=qx[head],y=qy[head];
        head++;
        for(int i=0;i<4;i++)
           {
            int tx=x+dx[i],ty=y+dy[i];
            if(tx>=1&&ty>=1&&tx<=n&&ty<=m&&!vis[tx][ty]&&a[x][y]<a[tx][ty])
              {
                qx[++tail]=tx;
                qy[tail]=ty;
                vis[tx][ty]=1;
              }
           }
    }
    for(int i=m;i>=1;i--)
       if(vis[1][i])
         return i;
    return 0;
}
void booms(int p)
{
    int tail=1,head=1;
    qx[1]=1;
    qy[1]=p;
    viss[1][p]=1;
    while(head<=tail)
    {
        int x=qx[head],y=qy[head];
        head++;
        for(int i=0;i<4;i++)
           {
            int tx=x+dx[i],ty=y+dy[i];
            if(tx>=1&&ty>=1&&tx<=n&&ty<=m&&!viss[tx][ty]&&a[x][y]>a[tx][ty])
              {
                qx[++tail]=tx;
                qy[tail]=ty;
                viss[tx][ty]=1;
              }
           }
    }
}
inline int read()
{
    int r=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') w=~w+1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        r=(r<<3)+(r<<1)+ch-(1<<4)-(1<<5);
        ch=getchar();
    }
    return r*w;
}
int main()
{
    //freopen("P1514_5.in","r",stdin);
    n=read();m=read();
    for(int i=1;i<=n;i++)
       for(int j=1;j<=m;j++)
          a[i][j]=read();
    for(int i=1;i<=m;i++)
        booms(i);
    ans=0;
    for(int i=1;i<=m;i++)
        if(!viss[n][i])
            ans++;
    if(ans){
      cout<<0<<endl<<ans;
      return 0;
    }
    for(int i=1;i<=n;i++)
       for(int j=1;j<=m;j++)
          vis[i][j]=viss[i][j]=0;
    int right=1;
    while(!viss[n][m]&&!vis[n][m])
      {
        int x=boom(right);
        booms(x);
        for(int i=m;i>=1;i--)
           if(!viss[n][i])
            right=i;
        ans++;
      }
    cout<<1<<endl<<ans;
    return 0;
}
上一篇:Word Break(C++单词拆分)


下一篇:单词拆分