汽车拉力比赛

汽车拉力比赛

https://www.luogu.com.cn/problem/P2658

这道题给大家介绍三种做法(并查集也能做,但本蒟不会


First

算法:未知(不信你往下读)

同机房大佬在考试的时候想出来的神奇做法!!!

思路:对于每一个路标,求与之相连的四个格子与它的高度差中的最小值(边缘的不算)用tot来表示。然后从ans与tot中取较大值,也就是在所有路标对应的tot中取最大值。

什么?这是什么思路?能得分?答案是:这种思路实现出来,90分!(向所有绞尽脑计做出来还没有90分的同志表示致敬QAQ,我最开始深搜+二分才可怜的10分啊)

PS:据那位同学说,他是在看样例的时候发现的这个“规律”,想着反正打不出正解,能得多少算多少.......

code1:

#include <bits/stdc++.h>
using namespace std;
int m,n,a[501][501],lb[501][501],tot,ans; 
int main() {
	scanf("%d%d",&m,&n);
	for(register int i=1;i<=m;i++) {
		for(register int j=1;j<=n;j++) {
			scanf("%d",&a[i][j]);
		}
	}
	for(register int i=1;i<=m;i++) {
		for(register int j=1;j<=n;j++) {
			scanf("%d",&lb[i][j]);
		}
	}
	for(register int i=1;i<=m;i++) {
		for(register int j=1;j<=n;j++) {
			if(lb[i][j]==1) {
				tot=0x3f3f3f3f;
				if(a[i][j-1]!=0) tot=min(tot,abs(a[i][j-1]-a[i][j]));
				if(a[i-1][j]!=0) tot=min(tot,abs(a[i-1][j]-a[i][j]));
				if(a[i+1][j]!=0) tot=min(tot,abs(a[i+1][j]-a[i][j]));
				if(a[i][j+1]!=0) tot=min(tot,abs(a[i][j+1]-a[i][j]));
			}
			ans=max(ans,tot);
		}
	}
	printf("%d",ans);
	return 0;
}

Second

算法:深搜DFS+二分

思路:

1、用lb数组专门存储路标的坐标,用u累计路标个数

2、二分(注意:一定是从0开始枚举!)每次枚举的就是mid值,然后从第一个路标开始深搜

3、深搜中,遇到一个点就用p数组标记为true

4、深搜完,判断所有路标是否都被标记为true。如果有false,那么当前mid就不符合题意,修改左端点=mid+1。否则,修改右端点=mid-1,并将当前mid值保存在ans中。最后输出ans即可

嗯,是中规中矩的思路了!个人认为,程序易懂qvq

code2:

#include <bits/stdc++.h>
using namespace std;
int n,m,ans,u;
long long l=1,r,a[505][505];
int dx[4]={0,0,-1,1};
int dy[4]={-1,1,0,0};
bool p[505][505]; 
struct node {
	int x,y;
}lb[250025];

inline void dfs(int x,int y,int kk) {
	if(p[x][y]) return ;
	p[x][y]=true; //将当前点标记为访问过 
	for(register int i=0;i<4;i++) { //上下左右四个方向 
		int xx=x+dx[i];
		int yy=y+dy[i];
		if(xx<1||xx>m||yy<1||yy>n||abs(a[xx][yy]-a[x][y])>kk) continue; //没有越界 
		dfs(xx,yy,kk);
	}
	return ;
}

int main() {
	scanf("%d%d",&m,&n);
	for(register int i=1;i<=m;i++) {
		for(register int j=1;j<=n;j++) {
			scanf("%lld",&a[i][j]);
			r=max(r,a[i][j]); //最高海拔即为枚举的右端点,很显然任意两点海拔值都不可能比最高海拔值大 
		}
	}
	for(register int i=1;i<=m;i++) {
		for(register int j=1;j<=n;j++) {
			int s;
			scanf("%d",&s);
			if(s==1) {
				lb[++u].x=i; //u累计路标总数;lb数组记录路标坐标 
				lb[u].y=j;
			}
		}
	}
	while(l<=r) { //二分枚举 
		int mid=(l+r)>>1;
		bool pk=true;
		memset(p,false,sizeof(p)); //记住每次枚举都要清零 
		dfs(lb[1].x,lb[1].y,mid); //从1号路标开始 
		for(register int i=1;i<=u;i++) { 
			if(p[lb[i].x][lb[i].y]==false) { //如果有路标没有被访问,即表示当前mid不符合题意,直接跳出 
				pk=false;
				break;
			}
		}
		if(pk==false) l=mid+1; //修改两个端点 
		else r=mid-1,ans=mid;
	}
	printf("%d",ans);
	return 0;
} 

Third

算法:广搜BFS+二分

思路:和深搜的基本一致,除了多用了st、en标记第一个路标的坐标,剩下的就是套广搜板子了。直接看代码吧qvq

#include <bits/stdc++.h>
using namespace std;
bool lb[505][505],pd=1,p[505][505];
int n,m,a[505][505],l,r,mid,ans,st,en,tp; 
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};

inline bool bfs() { //直接套广搜板子 
    queue <int> x, y; 
    int sum=1;
    x.push(st);
	y.push(en);
    p[st][en]=1;
    while(!x.empty ()) {
        int xx=x.front(),yy=y.front();
        if(sum==tp) return 1; //所有路标都被包含了就可以返回true了 
        x.pop();
		y.pop();
        for(register int i=0;i<4;i++) {
            int xxx=xx+dx[i];
			int yyy=yy+dy[i];
            if(xxx<1||xxx>n||yyy<1||yyy>m||p[xxx][yyy]||abs(a[xxx][yyy]-a[xx][yy])>mid) continue;
            if(lb[xxx][yyy]==1) sum++; //统计覆盖到的路标 
            x.push(xxx); //入队 
			y.push(yyy);
            p[xxx][yyy]=1;
        }
    }
    return 0;
}

int main () {
    scanf("%d%d",&n,&m);
    for(register int i=1;i<=n;i++) {
        for(register int j=1;j<=m;j++) {
            scanf("%d",&a[i][j]);
            r=max(r,a[i][j]);
        }
    }
    for(register int i=1;i<=n;i++) {
        for(register int j=1;j<=m;j++) {
            scanf("%d",&lb[i][j]); 
            if(lb[i][j]==1) tp++;
            if(pd==1&&lb[i][j]==1) { //标记第一个路标 
                st=i;
				en=j;
				pd=0;
            }
        } 
    }
    while(l<=r) {
        mid=(l+r)>>1;
        memset(p,0,sizeof p); 
        if(bfs()==true) {
        	r=mid-1;
        	ans=mid;
		}
        else l=mid+1;
    } 
    printf("%d",ans); 
    return 0;
}

总结一下:

1、深搜和广搜的板子是肯定要背住的,遇到就直接套

2、二分要注意枚举的边界,这样可以节省很多时间耗费

PS:也许会有很多人说第一种做法90分是因为数据水,这确实是一个因素。但是在考试或者比赛时,谁也不知道数据是什么样的,要是能用这样简单的程序得高分,何乐不为?所以,运气也是实力的一部分(我就没发现这种做法,再次膜拜同机房大佬Orz)

哈哈,扯远了。那那那那就希望这篇题解对您有些许的帮助吧QVQ~~


posted @ 2020-06-08 13:23  Eleven谦  阅读(205)  评论(0编辑  收藏  举报