P1514 [NOIP2010 提高组] 引水入城

dp/记忆化搜索练习

题目大意:有n行m列城市,第一行城市靠海,第n行城市靠沙漠,因为最后一行城市靠沙漠,所以需要第一行的城市建立蓄水厂运输水源到达最后一行,一个城市可以把水源往他的四周运输水源,其一个城市能把水运送到另一个城市的条件为这个城市的高度大于他要运输的城市的高度。问你能不能通过第一行的蓄水厂把水运输到最后一行的所有城市,如果不可以,输出0,然后输出至少有多少个城市不能运输到水:如果可以,输出需要的最少的蓄水厂。

思路:首先可以用dfs向四周搜一下,对搜到的点进行一个标记,如果最后一行的城市都标记上了就说明是都可以的,如果没有标记上则输出未标记的城市就好了:

如果都标记上,那么我们需要输出需要最少的蓄水厂的数量,对于所有城市都能标记的条件下,

每个蓄水厂能走到的距离的范围一定是连续的,因为只要有一个蓄水厂的能搜索到的城市是间断的,那么其他的就说明间断的城市中间一定没有城市能大于这些地方。然后因为是连续的,所以我们可以保存每一个蓄水厂能够送水的最短区间和最长区间,然后进行一个一个一个的dp。然后怎么找这个区间呢,就可以在我们的dfs中,用l[x][y]代表这个点能流向的最左边的点,r同理是最右边的点,在每次搜索时dfs一遍就可以啦,记住有一点无论能不能进行dfs那个点都是要进行更新的,因为dfs只是代表你搜过了,但是更新操作还是需要的(当然这个点前提要合法)。

然后我们得到了每个蓄水厂能够流到的连续区间,那么我们可以从长度为第一个蓄水厂开始,每次循环所有的蓄水厂(第一行的),如果当前蓄水厂的最小值小于我的当前的走到的长度,就可以把最能够走到的最右边的长度更新,然后每次更新长度时直接res++即可。

最后记得要初始化数组qwq

/**
*  ┏┓   ┏┓+ +
* ┏┛┻━━━┛┻┓ + +
* ┃       ┃
* ┃   ━   ┃ ++ + + +
*  ████━████+
*  ◥██◤ ◥██◤ +
* ┃   ┻   ┃
* ┃       ┃ + +
* ┗━┓   ┏━┛
*   ┃   ┃ + + + +Code is far away from  
*   ┃   ┃ + bug with the animal protecting
*   ┃    ┗━━━┓ 神兽保佑,代码无bug 
*   ┃  	    ┣┓
*    ┃        ┏┛
*     ┗┓┓┏━┳┓┏┛ + + + +
*    ┃┫┫ ┃┫┫
*    ┗┻┛ ┗┻┛+ + + +
*/

#include<cstdio>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <string>
#include <math.h>
#include<vector>
#include<queue>
#include<map>
#define sc_int(x) scanf("%d", &x)
#define sc_ll(x) scanf("%lld", &x)
#define pr_ll(x) printf("%lld", x)
#define pr_ll_n(x) printf("%lld\n", x)
#define pr_int_n(x) printf("%d\n", x)
#define ll long long 
using namespace std;

const int N=1000+100;
int n ,m,h;
ll s[N][N];
int l[N][N],r[N][N];
bool st[N][N];
int dirx[5]={0,1,0,-1,0};
int diry[5]={0,0,1,0,-1};


void dfs(int x,int y)
{
    st[x][y]=true;
    for(int i =1;i<=4;i++)
    {
      int xx=x+dirx[i],yy=diry[i]+y;
      if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&s[x][y]>s[xx][yy])
      {
        if(!st[xx][yy])
        dfs(xx,yy);
        l[x][y]=min(l[x][y],l[xx][yy]);
        r[x][y]=max(r[x][y],r[xx][yy]);
      }
    }
  return ;
}

void putt(int x)
{
  pr_int_n(0);
  pr_int_n(x);
  return ;
}

int main()
{
  memset(l, 0x3f, sizeof(l));
  memset(r, 0, sizeof(r));  
	int t;
	sc_int(n),sc_int(m);
  for(int i =1;i<=n;i++)
    for(int j =1;j<=m;j++)
    {
      sc_int(s[i][j]);
      if(i==n)
      l[i][j]=r[i][j]=j;
    }

  for(int i =1;i<=m;i++)
  {
    if(!st[1][i])
    dfs(1,i);
  }

  bool flag=false;
  int sum=0;
  for(int i =1;i<=m;i++)
  {
    if(!st[n][i])
    {
      flag=true;
      sum++;
    }
  }
  if(flag)putt(sum);
  else {
    int L=l[1][1],R=r[1][1];
    sum=0;
    while(L<=m)
    {
      for(int i =1;i<=m;i++)
      {
        if(l[1][i]<=L)
        R=max(R,r[1][i]);
      }
      L=R+1;
      sum++;
    }
  pr_int_n(1);
  pr_int_n(sum);
  }
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值