引水入城

只能给超链接了

https://www.luogu.org/problem/show?pid=1514
解释:bfs+dp

枚举第一行每个点进行bfs,算出每个点能扩展到最后一行的左端点与右端点。这里有一个剪枝:如果该点的海拔>=左右两点和下面的点的海拔那么可选,其它情况选了也是重复搜索。

至于左端点与右端点…首先我们要证明如果输出的是1的话,那么扩展到最后一行一定是连续的一段区间。

对于最后一行的一个点i,如果它的左边和右边都到达了,而它却无法到达,那么海拔比它高的只有它的上面了。设它上面的点为j,如果我们想到达j,那么我们仍然只能从j的上面到达,因为如果是由j的左右到达的话,那么i点的左右必定也能到达。一次类推,发现i所在的一列就变成了一个“屏障”,左边的点流不到右边,右边流不到左边,只有这一列能流向两边…与假设违背啊...

所以最后有了许多线段,转换成了区间覆盖问题,贪心法求解。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
int c[4][2]={{1,0},{-1,0},{0,-1},{0,1}};
int a[510][510];
bool d[510][510],e[510][510];
struct hh
{
    int x,y;
}p[510];
int cmp(hh a,hh b)
{
    if (a.x==b.x) return a.y<b.y;
    else return a.x<b.x;
}
int main()
{
    int n,m,i,j;
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;i++)
      for (j=1;j<=m;j++)
        scanf("%d",&a[i][j]);
    for (j=1;j<=m;j++)
      if (a[1][j]>=a[1][j+1] && a[1][j]>=a[1][j-1] && a[2][j]<a[1][j])
    {
        memset(e,0,sizeof(e));
        queue <int> x,y;
        d[1][j]=true;
        x.push(1);
        y.push(j);
        p[j].x=2100000;
        while (!x.empty())
        {
        int u=x.front(),v=y.front();
        if (u==n)
        {
            p[j].x=min(p[j].x,v);
            p[j].y=max(p[j].y,v);
        }
        x.pop();y.pop();
        for (i=0;i<=3;i++)
        {
            int ii=c[i][0]+u,jj=c[i][1]+v;
            if (ii>0 && jj>0 && ii<=n && jj<=m && a[ii][jj]<a[u][v] && !e[ii][jj])
            {
                e[ii][jj]=true; d[ii][jj]=true;
                x.push(ii); y.push(jj);
            }
        }
        }
    }
    int dd=0;
    for (i=1;i<=m;i++)
      if (!d[n][i]) dd++;
    if (dd) 
    {
       printf("0\n%d",dd);    
       return 0;
    }
    printf("1\n");  
    sort(p+1,p+1+m,cmp);
    int cur=1,ans_cnt=0;
    i=2;
    while(p[cur].y<m)
        {
            int maxk=cur;
            while(i<=m&&p[i].x<=p[cur].y+1)
            {
                if(p[i].y>p[maxk].y) maxk=i;
                i++;
            }
            cur=maxk;
            ans_cnt++;
        }
    printf("%d\n",ans_cnt);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值