NOIP2010【引水入城】

10 篇文章 0 订阅
7 篇文章 0 订阅

【题解】

  起先搜索将整张图遍历一遍,求出每个第一行的格子能覆盖到的最后一行的最左和最右的格子用g[1][j][0/1]表示(刚开始用广搜(注释的部分),每次求一个格子都遍历一边(比较蠢)复杂度是o(n*m^2)只能过七十分,后来改成了深搜只遍历一遍算出所有,复杂度o(n*m))

  算出了上述的东西后,就是一个经典的区间覆盖动归(用第一行覆盖最后一行)

  f[i]表示最后一行前i的格子都已经被覆盖时所需要的第一行格子的最小值

  f[i]=min(f[i],f[g[1][j][0]-1]+1)
  详见代码

   

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cstdio>
#include <queue>
#include <ctime>
#include <cmath>
#include <vector>
using namespace std;
int i,j,k,l,m,n;
int f[505],flag[505][505],q[50000][2],g[505][505][2],h,t,a[505][505],xx[4]={-1,0,1,0},yy[4]={0,1,0,-1};
void dfs(int x,int y)
  {
	int x2,y2;
	flag[x][y]=1;
	if (x==n) g[x][y][0]=g[x][y][1]=y;
	for (int i=0;i<4;i++)
	  {
		x2=x+xx[i];
		y2=y+yy[i];
		if (a[x][y]>a[x2][y2])
		  {
		    if (!flag[x2][y2]) dfs(x2,y2);
			g[x][y][0]=min(g[x][y][0],g[x2][y2][0]);
			g[x][y][1]=max(g[x][y][1],g[x2][y2][1]);
		  } 	
	  }  
  }

int main()
  {
  	scanf("%d%d",&n,&m);
  	for (i=1;i<=n;i++) for (j=1;j<=m;j++) scanf("%d",&a[i][j]);
  	for (i=0;i<=m;i++) a[0][i]=a[n+1][i]=1e9;
  	for (i=0;i<=n;i++) a[i][0]=a[i][m+1]=1e9;
  	for (i=1;i<=n;i++) for (j=1;j<=m;j++) g[i][j][0]=m+1,g[i][j][1]=0;
  	/*for (i=1;i<=m;i++)
  	  {
  	  	t++;q[t][0]=1;q[t][1]=i;flag[1][i]=1;
      }
  	  	for (;h<t;)
  	  	  {
  	  	  	h++;
  	  	  //	if (q[h][0]==n) g[i][0]=min(g[i][0],q[h][1]);g[i][1]=max(g[i][1],q[h][1]);
  	  	  	for (j=0;j<4;j++)
  	  	  	  if (a[q[h][0]+x[j]][q[h][1]+y[j]]<a[q[h][0]][q[h][1]]&&flag[q[h][0]+x[j]][q[h][1]+y[j]]==0)
  	  	  	    {
					flag[q[h][0]+x[j]][q[h][1]+y[j]]=i;
					t++;q[t][0]=q[h][0]+x[j];q[t][1]=q[h][1]+y[j];
					g[q[h][0]][q[h][1]][0]=min(g[q[h][0]][q[h][1]][0]
				}
		  }*/
    for (i=1;i<=m;i++) dfs(1,i);
	l=0;
	for (i=1;i<=m;i++)
	  if (flag[n][i]==0) l++;
	if (l>0) {printf("0\n%d",l);return 0;}
	
    for (i=1;i<=m;i++)
      {
        f[i]=1e9;
        for (j=1;j<=m;j++)
            if (i>=g[1][j][0]&&i<=g[1][j][1])
                f[i]=min(f[i],f[g[1][j][0]-1]+1);
      }
	printf("1\n%d",f[m]);  
  }



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值