宽搜+贪心
还是挺容易的
对第一行的每一个点进行搜索,统计最后一行每个点的到达情况
然后记录下该点可到达的最左端和最右端的位置
判断最后一行的到达情况,统计不可到达的点,如果不为0就输出,退出程序
否则就是做最少线段覆盖问题
可以证明第一行的每一个点可到达最后一行的位置是连续的(我证不来)
问题就变成了完全覆盖
把线段以头为主要关键字从小到大,尾为次要关键字从大到小排序
设当前线段可覆盖
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define g getchar()
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
inline ll read(){
ll x=0,f=1;char ch=g;
for(;ch<'0'||ch>'9';ch=g)if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=g)x=x*10+ch-'0';
return x*f;
}
inline void out(ll x){
int a[25],t=0;
if(x<0)putchar('-'),x=-x;
for(;x;x/=10)a[++t]=x%10;
for(int i=t;i;--i)putchar('0'+a[i]);
if(t==0)putchar('0');
putchar('\n');
}
struct re{int x,y;}s[505],dui[505*505];
int n,m,a[505][505],tou,wei,ans,now,nx;
bool mp[505][505],pd[505];
int ox[5]={0,1,0,-1,0},oy[5]={0,0,1,0,-1};
inline bool cmp(re x,re y){return x.x==y.x?x.y<y.y:x.x<y.x;}
int main(){
// freopen("1.in","r",stdin);
// freopen("","w",stdout);
n=read();m=read();
memset(a,inf,sizeof(a));
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
a[i][j]=read();
memset(s,inf,sizeof(s));
for(int i=1;i<=m;++i){
if(!((i==1||i==m)||(a[1][i]>=a[1][i-1]&&a[1][i]>=a[1][i+1])))continue;
dui[tou=wei=1]=(re){1,i};
memset(mp,0,sizeof(mp));mp[1][i]=1;
for(;tou<=wei;++tou){
int x=dui[tou].x,y=dui[tou].y;
for(int j=1;j<=4;++j){
if(mp[x+ox[j]][y+oy[j]])continue;
if(a[x][y]<=a[x+ox[j]][y+oy[j]])continue;
mp[x+ox[j]][y+oy[j]]=1;
dui[++wei]=(re){x+ox[j],y+oy[j]};
}
}
for(int j=1;j<=m;++j){
pd[j]=(pd[j]||mp[n][j]);
if(mp[n][j]&&!mp[n][j-1])s[i].x=j;
if(mp[n][j]&&!mp[n][j+1])s[i].y=j;
}
}
for(int i=1;i<=m;++i)ans+=pd[i];
if(ans<m){out(0),out(m-ans);return 0;}
sort(s+1,s+1+m,cmp);
now=1;nx=0;ans=0;
for(;now<=m;){
int i,tmp=0;
for(i=1;i<=m;++i){
if(now<s[i].x)break;
if(tmp<s[i].y)tmp=s[i].y;
}
now=tmp+1;++ans;
}
out(1),out(ans);
return 0;
}