引水入城(记忆化搜索+区间完全覆盖问题)

15 篇文章 0 订阅
5 篇文章 0 订阅

每个第一行的城市,在最后一列能影响到的一定是一段连续区间。如果不连续,假设是两段区间,那么两段区间中肯定不能被其他第一行的城市影响到,如果影响到,如下图

如果蓝色和红色相交,那么蓝色一定可以更新到红色的。所以假设是两段区间,那么两段区间中肯定不能被其他第一行的城市影响到,而这种情况就是题目所谓不合法情况。

那么我们就记忆化搜索,把第一行每个城市能影响到的最后一行的连续区间大小处理出来。

然后就是求一个区间完全覆盖问题。

按左端点排序,贪心思想,每次选左端点在已覆盖区间内,右端点最远的。

shang:目前已经覆盖到的区间的最右边。

now:剩下的线段中左端点在shang的左边或和shang一起的线段的右端点的最大值。

注意啊!我们的区间看成右开左闭,shang从0开始,就当作背个板子吧~~~~~。。。。。=。=

#include<bits/stdc++.h>
using namespace std;
int n,m,a[595][595],vis[595][595],l[595][595],r[595][595];
int zl[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
struct node
{
	int l,r;
};
node q[595];
void read(int &x)
{
	x = 0;  int f = 0;  char c = getchar();
	while(c < '0' || c > '9')
	{
		if(c == '-') f = 1; 	c  = getchar();
	}
	while(c >= '0' && c <= '9')
	{
		x = x * 10 + c - '0'; c = getchar();
	}
	if(f) x = -x ;
}
bool cmp(const node a,const node b)
{
	return a.l < b.l;
}
void dfs(int x,int y)
{
	if(vis[x][y]) return;
	vis[x][y] =  1;
	for(int k = 0; k <= 3; k++)
     {
     	int xx  = x + zl[k][0];
     	int yy = y + zl[k][1];
     	if(xx >= 1 && xx <= n && yy >= 1 && yy <= m&&a[xx][yy] < a[x][y])
     	{
     		if(vis[xx][yy] == 0) dfs(xx,yy);
     		l[x][y] = min(l[xx][yy],l[x][y]);
     		r[x][y] = max(r[x][y],r[xx][yy]);
     	}
     }
}
int main()
{
	//freopen("ha.in","r",stdin);
      read(n);read(m);
	  for(int i = 1; i <= n; i++) 
	   for(int j = 1; j <= m; j++)
	    read(a[i][j]);	
       memset(vis,0,sizeof(vis));
       memset(l,0x3f3f3f3f,sizeof(l));
	   for(int i = 1; i <= m; i++)
	     l[n][i] = r[n][i] = i; 
	  for(int i = 1; i <= m; i++)
	        dfs(1,i); 
	        int cnt = 0;
	    for(int i = 1; i <= m; i++)
	     if(vis[n][i] == 0) cnt++;
	 if(cnt)
	 {
	 printf("0\n");	printf("%d",cnt);return 0;
	 }
	 printf("1\n");
	 for(int i = 1; i <= m; i++)
	 {
	if(r[1][i]){cnt++;
		q[cnt].l = l[1][i] - 1; q[cnt].r = r[1][i];	
	}
     }
	 sort(q+1,q+1+cnt,cmp);
	 int now = 0;
	int ans = 0,shang = 0;
     for(int i = 1; i <= cnt; i++)
     {
     	if(q[i].l > shang){ans++,shang = now;
    	}
         now = max(now,q[i].r);
     }
     if(shang < m)ans++;
     cout << ans;
     return 0;
} 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值