题目描述
n*m的矩阵只有第一行能走,每个位置有一个数字,只能从大数字走向小数字,如果能走完最后一行,输出第一行1,第二行最少要从第一行几个点出发
如果不能,输出0\n有多少个点不能走到.
输入样例#1
2 5
9 1 5 4 3
8 7 6 1 2
输出样例#1
1
1
输入样例#2
3 6
8 4 5 6 4 4
7 3 4 3 3 3
3 2 2 1 1 2
输出样例#2
1
3
分析
一开始想bfs+贪心。
dp也行
但是不要套算法,要找出题目的特性。太菜以至于没能发现这个特性:如果最后一行走的不是一个连续的区间,那么必定不可行。这样就不要拆分区间了,只要记录区间最左和最右就好。
- 大致框架:
- 方向数组dx,dy
- 访问过标记数组isok
- 判断能否走完最后一行bool ok
- f数组记录最后一行的遍历情况以便求出不行的情况下有多少不能走到。(扫一遍最后一行就好)
- 结构体,能覆盖的左右区间 l,r。
- 如果是贪心要 cmp ,按l和区间长度排,+sort 。区间覆盖问题。
- 如果是 dp 要 d【i】表示覆盖到i最少要多少个区间。
- 按道理dp跟贪心 时间复杂度都是n^2,但是dp常数更好。
10. 代码很长,只给关键部分:
CODE
dx[]={1,-1,0,0},dy[]={0,0,1,-1};
f[600],ok;
struct node{
int l, r;
}a[600];
bool cmp(node a,node b){
if(a.l<b.l) return 1;
else
{
if(a.l==b.l)
{
if(a.r-a.l>b.r-b.l)return 1;
else return 0;
}
else return 0;
}
}
int ap[600][600],isok[600][600];
void bfs(int y){
int q[600000][2];
memset(isok,0,sizeof(isok));
int minn=m+1,maxn=0;
int h=0,t=1;
q[t][0]=1;q[t][1]=y;isok[1][y]=1;
while(h<t)
{
h++;
int xx=q[h][0],yy=q[h][1];
if(xx==n)
{
minn=min(minn,yy);
maxn=max(maxn,yy);
f[yy]=1;
}
for(int i=0;i<4;i++)
{
int tx=xx+dx[i],ty=yy+dy[i];
if(tx>=1&&tx<=n&&ty>=1&&ty<=m)
if(ap[tx][ty]<ap[xx][yy])
{
if(!isok[tx][ty]){
isok[tx][ty]=1;
q[++t][0]=tx,q[t][1]=ty;
}
}
}
}
a[y].l=minn;
a[y].r=maxn;
}
//这是贪心做法
int dp(){
int now=0,to=0,tot=0;
sort(a+1,a+m+1,cmp);
for(int i=1;i<=m;i++)
{
if(now+1>=a[i].l)
to=max(to,a[i].r);
else
now=to,to=max(to,a[i].r),tot++;
}
if(now!=m)tot++;
return tot;
}
//这是dp做法
int dp(){
int d[600];
memset(d,12,sizeof(d));
d[0]=0;
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++)
if(a[j].l<=i&&a[j].r>=i)
d[i]=min(d[i],d[a[j].l-1]+1);
return d[m];
}
int main()
{
1.输入ap
ok=1;
for(int i=1;i<=m;i++)
if(!isok[1][i])//如果不能能从其他行走到这里才bfs。
bfs(i);
int noans=0;//算有多少个不能走到。
for(int i=1;i<=m;i++)
if(!f[i]){noans++;ok=0;}
if(ok){printf("1\n%d\n",dp());}
else
printf("0\n%d",noans);
}