【总结】bfs

A. [USACO18OPEN,Silver]Multiplayer Moo

解析:这题挺水的。

首先跑一个bfs求连通块,附上对应的连通块标号,大小,颜色(及数字)。对相邻的连通块连边。然后枚举选择的两个颜色,对这两种颜色的连通块再求一次连通块即可。

乍一看时间复杂度 O ( m 2 c n t ) O(m^2cnt) O(m2cnt),其中cnt是连通块数目,m是颜色数目。而 c n t , m < = 25 0 2 cnt,m<=250^2 cnt,m<=2502,显然超时。

cnt是不能优化的,这是求连通块的时间代价。而枚举m时可以只枚举颜色数前50个大的,这样就过了。

#include<cstdio>
#include<queue>
#include<algorithm>
#include<cstring>
#include<map>

using namespace std;
const int N=255,M=255*255;

map<int,int> mp;
int ans,n,cnt,con[N][N],a[N][N],col[M],siz[M],lsh[M],num,sum[M];
int dx[5]={-1,0,1,0},dy[5]={0,1,0,-1};
bool pos[M];
vector<int> son[M];

bool cmp(int x,int y) {
	return sum[x]>sum[y];
}


void bfs(int x,int y) {
	queue<pair<int,int> > q;
	q.push(make_pair(x,y));
	con[x][y]=++cnt;
	col[cnt]=a[x][y];
	siz[cnt]=1;
	while(q.size()) {
		int x=q.front().first,y=q.front().second;
		q.pop();
		for(int i=0;i<4;i++) {
			int tx=x+dx[i],ty=y+dy[i];
			if(!con[tx][ty]&&a[x][y]==a[tx][ty]) {
				q.push(make_pair(tx,ty));
				con[tx][ty]=cnt;
				siz[cnt]++;
			}
		}
	}
}

void bfs2(int x,int a,int b) {
	int tot=siz[x];
	queue<int> q;
	q.push(x);
	pos[x]=1;
	while(q.size()) {
		int x=q.front();q.pop();
		for(int i=0;i<son[x].size();i++) {
			int y=son[x][i];
		    if(!pos[y]&&y>0&&(col[y]==lsh[a]||col[y]==lsh[b])) {
			    q.push(y);
			    pos[y]=1;
			    tot+=siz[y];
		    }
	    }
	}
	ans=max(ans,tot);
}

int main() {
	scanf("%d",&n);
	for(int i=1;i<=n;i++) 
		for(int j=1;j<=n;j++) {
			scanf("%d",&a[i][j]);
			if(!mp[a[i][j]]) {
				mp[a[i][j]]=++num;
				lsh[num]=num;
			}
			a[i][j]=mp[a[i][j]];
			sum[a[i][j]]++;
		}
		    
	for(int i=1;i<=n;i++)
	    for(int j=1;j<=n;j++) {
	    	if(!con[i][j]) {
	    		bfs(i,j);
	    		ans=max(ans,siz[con[i][j]]);
			}
		}
	for(int i=1;i<=n;i++)
	    for(int j=1;j<=n;j++) {
	    	for(int s=0;s<4;s++) {
	    		int k=i+dx[s],l=j+dy[s];
	    		int x=con[i][j],y=con[k][l];
	    		if(x!=y) {
	    			son[x].push_back(y);
	    			son[y].push_back(x);
				}
			}
		}
	printf("%d\n",ans);
	sort(lsh+1,lsh+1+num,cmp);
	for(int i=1;i<=min(num,50);i++) {
		for(int j=1;j<=min(num,50);j++) {
			if(i==j) continue;
			for(int k=1;k<=cnt;k++) pos[k]=0;
			for(int k=1;k<=cnt;k++) {
				if(!pos[k]&&(col[k]==lsh[i]||col[k]==lsh[j])) {
					bfs2(k,i,j);
				}
			}
		}
	}
	printf("%d",ans);
}

B. 1-04D. 膨胀的tyx

解析:一道中规中矩的搜索题。

显然只在给定的图中遍历,用(x,y,h,l)四元组表示状态,其中(h,l)表示在大的平面内的横纵坐标。从起点开始bfs,x>n则h++,x=0则h --;y>n则l++,y=0则l --。

当到达以前走过的点时,判断h,l是否相等,不相等则输出No;否则不加入队列(重复状态)。搜索结束后就输出Yes。

#include<bits/stdc++.h>
using namespace std;
const int N=2005;

struct node{
	int x,y,h,l;
}st;


int n,m,h[N][N],l[N][N],pos[N][N];
int dx[5]={-1,0,1,0},dy[5]={0,1,0,-1};
char s[N][N];

void bfs() {
	queue<node> q;
	q.push(st);
	pos[st.x][st.y]=1;
	while(q.size()) {
		node x=q.front();q.pop();
		//printf("%d %d %d %d\n",x.x,x.y,x.l,x.h);
		for(int i=0;i<4;i++) {
			node y=x;
			int tx=y.x+dx[i],ty=y.y+dy[i];
			if(tx>n) tx-=n,y.h++;
			if(tx==0) tx+=n,y.h--;
			if(ty>m) ty-=m,y.l++;
			if(ty==0) ty+=m,y.l--;
			y.x=tx;
			y.y=ty;
			if(s[tx][ty]=='#') continue;
			if(!pos[tx][ty]) {
				pos[tx][ty]=1;
				l[tx][ty]=y.l;
				h[tx][ty]=y.h;
				q.push(y);
			}
			else if(y.l!=l[tx][ty]||y.h!=h[tx][ty]) {
				
				printf("Yes");
				exit(0);
			}
		}
	}
}

int main() {
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	    for(int j=1;j<=m;j++) {
	    	cin>>s[i][j];
	    	if(s[i][j]=='S') st.x=i,st.y=j;
		}
	bfs();	
	printf("No");
}

C. [树声前锋杯] E 小马过河

不会。

input:
50 1250
50 100 50 50 50 50 100 100 50 100 50 100 50 100 100 50 100 100 50 100 50 100 50 50 50 100 50 100 50 50 50 50 100 50 100 50 100 50 50 100 50 50 50 50 50 50 50 50 100 100 
output:
3

D. 2-04D. 懒得说话的tyx

解析:这道题我是用数位dp做的。

注意前导0,正常的数位dp中都用dfs中参数z表示是否有前导0,这里我把它拆分成f1和f2,f1没有前导零,f2有前导零。

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e5+5;


//11111110111010
//111111101110

int n,m,f1[101][N],f2[16][N],g[16],len,tot;

int main() {
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++) f1[1][i]=1;
	for(int i=0;i<n;i++) f2[1][i]=1;
	g[1]=1;
	f2[0][0]=1;
	for(int i=2;i<=15;i++) g[i]=g[i-1]*10%m;
	for(len=1;len<=15;len++) {
		if(f1[len][0]) break;
		for(int i=0;i<m;i++) {
			for(int j=0;j<n;j++) {
				f1[len+1][(i*10+j)%m]|=f1[len][i];
				f2[len+1][(i*10+j)%m]|=f2[len][i];
			}
		}
	}
	//printf("%d\n",len);
	for(int i=1;i<=len;i++) {
		//printf("%d\n",tot);
		for(int j=0;j<n;j++) {
			if(i==1&&j==0) continue;
			if(f2[len-i][(tot-(j*g[len-i+1]%m)+m)%m]) {
				printf("%d",j);
				tot=(tot-(j*g[len-i+1]%m)+m)%m;
				break;
			}
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
BFS(广度优先搜索)是一种图遍历算法,用于解决图上的问题。在Python中,可以使用队列来实现BFS。引用和引用中的代码示例展示了如何使用BFS遍历树节点的层级。 在代码示例中,首先需要根据给定的树结构创建一个队列,并将根节点入队。然后,使用一个循环来迭代处理队列中的节点。在每次循环中,先获取当前队列的长度n,然后创建一个临时列表temp用于存储当前层级的节点值。 接下来,使用一个for循环遍历当前层级的节点数n,依次从队列中弹出节点a,并将其值a.val添加到临时列表temp中。然后,将该节点的左右子节点(如果存在)加入队列中,以便在下一次迭代中处理。 最后,在每一次循环结束后,将临时列表temp添加到结果列表res中,表示当前层级的节点值已经遍历完毕。当队列为空时,表示已经遍历完整个树,可以返回结果列表res作为最终结果。 需要注意的是,由于队列是先进先出的特性,所以在遍历每一层节点时,可以根据需要选择是否需要翻转临时列表temp来保证节点值的顺序。引用中的示例代码展示了如何使用BFS遍历图。 总结起来,使用BFS可以实现对树节点的层级遍历,通过队列来辅助实现节点的入队和出队操作。每一次从队列中取出节点时,将其子节点加入队列以便后续遍历。这样就可以按照层级顺序遍历树的节点,并将节点值保存到结果列表中返回。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [广度优先算法(BFS)-python](https://blog.csdn.net/weixin_44571635/article/details/123891023)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* [基于python模拟bfs和dfs代码实例](https://download.csdn.net/download/weixin_38549721/14839350)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值