/* 没想到还有dp的事 只能水部分分了 前几个点可以水过 4,5暴力也可以 先每个临湖点都灌一下水 (可以加剪枝 比旁边小的可以不搜) 统计每个临湖点能灌倒几个临沙漠点 并记录每个临沙漠点是否能灌倒 这样前三个点就好办了 判断可以输出不能全灌到的点 对于剩下的全部能灌倒得 暴力的话是2^n 也就水两个点了 这里如果全部能灌倒 有一个很有用的性质 : 这时每个临水点能灌到的临沙漠点 一定是相邻的一段 简单反证一下吧 假设有两段不是连续的 那么不连续的那个部分一定会被越过去(形象点) 既然越过去了 介于灌水法的定义 也就是说从这个部分周围走却不覆盖 那显然不论从那个点开始灌 都覆盖不了 与题意矛盾 假设不成立 既然连续 那就记录下来 然后开始dp 显然他是个区间dp 一般的状态f[i][j]显然会爆(再加上枚举起点) 所以我们定义状态f[i]表示1-i全部覆盖最少几个临湖点 转移的话 枚举所有的临湖点 如果合法就更新 最后输出 */ #include<iostream> #include<cstdio> #include<cstring> #define maxn 510 using namespace std; int n,m,g[maxn][maxn],sum; bool f[maxn][maxn],des[maxn]; int xx[5]={0,0,0,1,-1}; int yy[5]={0,1,-1,0,0}; int dp[maxn]; struct node { int l,r,num,falg; }p[maxn]; void Dfs(int x,int y,int k) { f[x][y]=1; if(x==n) { des[y]=1; p[k].falg=1; p[k].num++; p[k].l=min(p[k].l,y); p[k].r=max(p[k].r,y); } for(int i=1;i<=4;i++) { int nx=x+xx[i]; int ny=y+yy[i]; if(nx>0&&nx<=n&&ny>0&&ny<=m&&f[nx][ny]==0&&g[x][y]>g[nx][ny]) Dfs(nx,ny,k); } } void DP() { memset(dp,127/3,sizeof(dp)); dp[0]=0; for(int i=1;i<=m;i++) { for(int j=1;j<=m;j++) { if(!p[j].falg)continue; if(p[j].l<=i&&p[j].r>=i)dp[i]=min(dp[i],dp[p[j].l-1]+1); } } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&g[i][j]); for(int i=1;i<=m;i++) { p[i].l=maxn+1; memset(f,0,sizeof(f)); Dfs(1,i,i); } for(int i=1;i<=m;i++) if(p[i].num==m) { printf("1\n1\n"); return 0; } for(int i=1;i<=m;i++) if(des[i])sum++; if(sum<m) { printf("0\n%d\n",m-sum); return 0; } DP(); printf("1\n%d\n",dp[m]); return 0; }