综合应用的好题:代码复杂度也比较符合t2的要求
题目大意:
1 n*m的地图,第一行是入口(水),第n行是出口(城市),每格都有个高度值,只能从高到低移动;
2 求用最少的入口,把所有的出口都联通。
解题思路:%HW同学,讲解细致!
1 通过宽搜,把每个入口能到达的出口找出来,记录入口的左右边界(l,r);
1.1 思考难点:如果一个入口不能到达连贯的区间的出口,那么中间的出口就永远无法到达;
1.2 如果地图只有一行(出口也是入口),要注意特判。
2 宽搜的时候,用v数组记录出口的到达情况,全部宽搜结束后判断,如果有出口未被到达,游戏结束0。
3 如果2能过了,就做DP,求用最小的区间实现完全覆盖。
上代码:ps很多细节处理,要认真阅读代码注解
#include<cstdio>
#include<cstring>
#include<cstdlib>
int n,m,tou,wei;
int ma[510][510],b[510][510];//地图
struct nod{int v,l,r;}a[510];//入口
int v[510];//出口
struct nodb{int x,y;}l[250005];//队列
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
void bfs(int sx,int sy)
{
memset(b,0,sizeof(b));//封路用
if(n==1) //一行的地图,特判
{
v[sy]=1;a[sy].l=a[sy].r=sy;
}
tou=1;wei=2;l[1].x=sx;l[1].y=sy;
b[sx][sy]=1;
while(tou<wei)
{
int x=l[tou].x;
int y=l[tou].y;
for(int i=0;i<4;i++)
{
int xx=x+dx[i];
int yy=y+dy[i];
if(ma[xx][yy]<ma[x][y]&&b[xx][yy]==0)
{
if(xx>=1&&xx<=n&&yy>=1&&yy<=m)
{
if(xx==1) a[yy].v=0;//入口被别的入口进入的话,就废除
if(xx==n)
{
v[yy]=1;//到达出口
if(a[sy].l>yy) a[sy].l=yy;//更新入口能到达的左右边界
if(a[sy].r<yy) a[sy].r=yy;
}
b[xx][yy]=1;//封路
l[wei].x=xx;//进队
l[wei].y=yy; wei++;
}
}
}tou++;
}
}
void so1()
{
//初始化
for(int i=1;i<=m;i++)
{
a[i].v=1;a[i].l=m+1;a[i].r=0;//初始化入口
v[i]=0;//初始化出口
}
//跑宽搜
for(int i=1;i<=m;i++)
{
if(a[i].v==1) bfs(1,i);
}
}
void so2() //特判:出口是否都能到达
{
int su=0;
for(int i=1;i<=m;i++)
{
if(v[i]==0) su++;
}
if(su!=0) { printf("0\n%d",su);exit(0); }
}
int minn(int x,int y) { return x<y?x:y; }
int f[510][510];//从i-j需要多少个入口才能覆盖
int d[510];//从 1-i,最少要多少个入口才能覆盖
void ddp()
{
//初始化
memset(f,63,sizeof(f));
memset(d,63,sizeof(d));
for(int k=1;k<=m;k++)
{
if(a[k].r==0) continue;//能到达出口的入口,才需要处理
for(int i=a[k].l;i<=a[k].r;i++)
{
for(int j=i;j<=a[k].r;j++)
{
if(i==1) d[j]=1;
f[i][j]=1;
}
}
}
//求区间最小
for(int i=1;i<=m;i++)
{
for(int j=1;j<i;j++)
{
d[i]=minn(d[i],d[j]+f[j+1][i]);
}
}
printf("1\n%d",d[m]);
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&ma[i][j]);
}
}
so1();//通过宽搜,将每个入口的左右边界求出来
so2();//特判,如果出口不完全到达,游戏结束
ddp();//dp求区间最小值
return 0;
}