zoukankan      html  css  js  c++  java
  • 汽车拉力比赛

    汽车拉力比赛

    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~~


  • 相关阅读:
    🔥低代码音视频开发训练营火热报名中!
    编解码再进化:Ali266 与下一代视频技术
    ICCV 2021口罩人物身份鉴别全球挑战赛冠军方案分享
    提升 RTC 音频体验 从搞懂硬件开始
    只要你有目标,只要你肯努力,成功只是时间问题
    安全感到底来自何方
    工作经验小结
    女人的出路在何方?
    那些以为过去了的
    初出茅庐
  • 原文地址:https://www.cnblogs.com/Eleven-Qian-Shan/p/13065142.html
Copyright © 2011-2022 走看看