从上向下记忆化搜索,保存每个点能覆盖的最底层的区间。如果一个点能到达的区间不连续,那么一定没有方案。然后做一个基础贪心:从n个区间中选出尽量少的区间,覆盖1-n
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 510
#define ll long long
#define max(x,y) ((x)>(y) ? (x) : (y))
#define min(x,y) ((x)<(y) ? (x) : (y))
using namespace std;
inline int getint() {
int t=0,p;
char ch=getchar();
for(;ch!='-' && !(ch>='0' && ch<='9');ch=getchar());
if(ch=='-') p=-1,ch=getchar();
else p=1;
for(;ch>='0' && ch<='9';ch=getchar()) {
t=t*10+ch-48;
}
return t*p;
}
struct Line {
int l,r;
Line() {
l=r=0;
}
bool operator < (const Line &o) const {
return l<o.l;
}
} b[N][N],c[N];
const int dx[4]={0,1,0,-1};
const int dy[4]={-1,0,1,0};
int a[N][N],n,m;
bool flag,f[N][N];
void Iscream(int x,int y) { //记忆化搜索,b[i][j] 保存每个点能覆盖的底层区间。
if(f[x][y]) return;
f[x][y]=true;
if(x==n) {b[x][y].l=y;b[x][y].r=y;}
for(int i=0;i<4;i++) {
int nx=x+dx[i];
int ny=y+dy[i];
if(nx>0 && nx<=n && ny>0 && ny<=m && a[nx][ny]<a[x][y]) {
if(!f[nx][ny]) Iscream(nx,ny);
if(b[nx][ny].l==0 && b[nx][ny].r==0) continue;
if(b[x][y].l==0 || b[x][y].r==0) {
b[x][y].l=b[nx][ny].l;
b[x][y].r=b[nx][ny].r;
} else {
if(b[x][y].r<b[nx][ny].l-1 || b[x][y].l>b[nx][ny].r+1) {flag=true;}
b[x][y].l=min(b[x][y].l,b[nx][ny].l);
b[x][y].r=max(b[x][y].r,b[nx][ny].r);
}
}
}
}
int main() {
// freopen("flow.in","r",stdin);
// freopen("flow.out","w",stdout);
n=getint();m=getint();
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
a[i][j]=getint();
}
}
flag=false;
memset(f,false,sizeof f);
for(int i=1;i<=m;i++) {
Iscream(1,i);
c[i]=b[1][i];
// printf("%d %d\n", b[1][i].l,b[1][i].r);
}
if(flag) {
int ans=0;
for(int i=1;i<=m;i++) {
ans+=!f[n][i];
}
printf("0\n%d\n",ans);
return 0;
}
int L=1;
sort(c+1,c+m+1);
int ans=0;
int j;
for(int i=1;i<=m;i=j) { //贪心策略
if(c[i].l>L) break;
ans++;
j=i+1;
int mx=c[i].r;
for(;j<=m && c[j].l<=L;j++) {
if(c[j].r<L) continue;
mx=max(mx,c[j].r);
}
if(mx>=L) L=mx+1;
if(L>m) break;
}
if(L<=m) {
int ans=0;
for(int i=1;i<=m;i++) {
ans+=!f[n][i];
}
printf("0\n%d\n",ans);
return 0;
}
printf("1\n%d\n", ans);
return 0;
}