队列(一本通)

1334:【例2-3】围圈报数

这道题重点是关系的转换和初始化

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int a[101];//记录接着的的那个节点
int n,m; 


int main(){
	cin>>n>>m;
	for(int i=1;i<n;i++) a[i]=i+1;
	a[n]=1;
	int p=0,head=1,j=n;//注意j的初值,因为一开始下面的写法 
	while(p<n){
		while(head<m){
			j=a[j];//接下 
			head++;
		}
		cout<<a[j]<<" "; //注意是接下来的那个退出,所以是a[j] 
		p++;
		a[j]=a[a[j]];//退出 
		head=1;//重新数 
	} 
return 0;
}

1335:【例2-4】连通块

很常见的一道题,经典

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int n,m;
int map[101][101];
int dis[101][101];
int queue[10001][2]; 
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int ans;
void bfs(int x,int y){
	int head=1;
	int rear=2;//这是队列!!!这两个总的有 
	queue[1][0]=x;queue[1][1]=y;
	while(head<rear){
		x=queue[head][0];
		y=queue[head][1];
		head++;
		for(int i=0;i<4;i++){
		int x1=x+dir[i][0];
		int y1=y+dir[i][1];
		if(x1>0&&x1<=n&&y1>0&y1<=m&&map[x1][y1]&&!dis[x1][y1]){
			dis[x1][y1]=1;
			queue[rear][0]=x1;
			queue[rear++][1]=y1;//入队 
		}
	}
	} 
	
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++) cin>>map[i][j];
	memset(dis,0,sizeof(dis));
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++){
		if(map[i][j]&&!dis[i][j]){
			bfs(i,j);
			ans++;
		}
	}
	cout<<ans<<endl;
return 0;
}

1359:围成面积

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
using namespace std;
//这道题得思路很难想,从边缘开始搜索,标记没有被1包围的0使他们和 1 成为同样的节点,最后枚举剩余的0即可 
int map[20][20];
int queue[101][2];
int dis[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
void bfs(int x,int y){
	int head=1,tail=0;
	map[x][y]=-1;//使这些0变为-1 
	queue[head][0]=x;
	queue[head][1]=y;
	while(head!=tail){
		tail++;
		for(int i=0;i<4;i++){
			int nx=queue[tail][0]+dis[i][0];
			int ny=queue[tail][1]+dis[i][1];
			if(map[nx][ny]==-1) continue;
			else map[nx][ny]=-1;
 			head++;
			queue[head][0]=nx;
			queue[head][1]=ny; 
		}
		
	}
}
int main(){
	int ans=0;
	memset(map,255,sizeof(map));  //首先要先初始化map为-1 !!!!!(255=-1) //能够在搜索的时候退出 
	for(int i=1;i<=10;i++)   //这里从1开始,是可以不用再bfs里面判断其下标合不合理 
	for(int j=1;j<=10;j++) {
		cin>>map[i][j];
		map[i][j]=-map[i][j];//注意这里的标记,既可以用来表示其代表的值,也可以用来表示有没有访问过 
	}//这里0还是0,1变为了-1 
	for(int i=1;i<=10;i++){
		if(!map[i][1]) bfs(i,1);
		if(!map[i][10]) bfs(i,10);
		if(!map[1][i]) bfs(1,i);
		if(!map[10][i]) bfs(10,i);//如果是0就开始搜索 
	}
	
	for(int i=1;i<=10;i++)
	for(int j=1;j<=10;j++) if(map[i][j]==0) ans++;//这个时候还有的0就是被包围的,因为从边缘搜索得到的0全部都被标记为了-1 
	cout<<ans<<endl;
return 0;
}

1360:奇怪的电梯(lift)

大楼的每一层楼都可以停电梯,而且第i层楼(1≤i≤N)(1≤i≤N) 上有一个数字Ki(0≤=Ki≤=N)Ki(0≤=Ki≤=N) 。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如:3 3 1 2 5代表了Ki(K1=3,K2=3,……)Ki(K1=3,K2=3,……) ,从一楼开始。在一楼,按“上”可以到44 楼,按“下”是不起作用的,因为没有−2−2 楼。那么,从AA 楼到BB 楼至少要按几次按钮呢?

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
using namespace std;
int pre[10001],aa[10001],vis[10001];
int sum[10001]; 
//pre表示当前的楼层(队列),a表示能上升和下降的数量 
//vis表示是否经过(保证最小距离)sum表示所用的次数!!!!(useful) 
int n,a,b;
int main(){
	cin>>n>>a>>b;
	for(int i=1;i<=n;i++) cin>>aa[i];
	int head=0,tail=1;
	pre[tail]=a;
	memset(vis,0,sizeof(vis));
	vis[a]=1;//确保只能去一次 
	do{
		head++;
		for(int i=-1;i<=1;i+=2){//模拟上下两次选择 
			if(pre[head]+i*aa[pre[head]]>=1&&pre[head]+i*aa[pre[head]]<=n&&!vis[pre[head]+i*aa[pre[head]]]){
				tail++;
				pre[tail]=pre[head]+i*aa[pre[head]];
				vis[pre[head]+i*aa[pre[head]]]=1;
				sum[tail]=sum[head]+1;//这个数列的作用就相当于queue[n][3] 
			}
		}
	}while(head<tail&&pre[head]!=b);//到达目标在这里判断 
	if(a==b) cout<<0<<endl;
	else if(pre[tail]==b) cout<<sum[tail]<<endl;
	else cout<<"-1"<<endl; 
return 0;
}

1361:产生数(Produce)

没有想到在一本通也有。。。。在洛谷上面做的数据更大一些,必须得换一个方法不然会爆掉

一本通:

给出一个整数n(n≤2000)和k个变换规则(k≤15)。

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<queue>
#include<cstdio>
using namespace std;
int n,k;
int be[16],af[16];
int vis[10001];
queue<int> qu;
int main(){
	cin>>n>>k;
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=k;i++) cin>>be[i]>>af[i];
	qu.push(n);
	vis[n]=1;
	int sum=1;
	while(!qu.empty()){
		int x=qu.front(),y=qu.front();
		cout<<qu.front()<<endl; 
		qu.pop();
		int mod=1;
		while(x>0){
			int temp=x%10;
			x/=10;
			for(int i=1;i<=k;i++){
				if(be[i]==temp){
					int yy=y+(af[i]-be[i])*mod;//因为是一位一位处理的 
					if(!vis[yy]){
						sum++;
						vis[yy]=1;
						qu.push(yy);
					}
				}
			}
			mod*=10;
		}
	}
	cout<<sum<<endl;
return 0;
}

洛谷上面:

数据是会超过long long的,所以必须用高精度

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
using namespace std;
//产生数
/*
很容易看出这是一道搜索题
考虑搜索方案,如果按字符串转移,必须存储每种状态,空间复杂度明显会爆炸

观察到每一位之间是互不影响的,考虑使用乘法原理
搜索出每一位的情况总数,求它们的连乘积即为答案!!!!!!注意数据很大 
时间复杂度O(n2^k)O(n2k)
可以看出答案最大可以达到三十的十次方,会爆掉longlong longlong,所以需要写高精
具体处理可以选择STL(懒得自己写)
对于映射,这是map的专长
如果一个数能够映射到多个数呢?
用map的时候从charchar映射到vector<char>vector<char>即可
*/
map<char,vector<char> > st;
int c[10],num[111],k;
string a;
//用来判断数字出现个数的,和记录结果的 
void dfs(char z){
	c[z-'0']=1;
	int size=st[z].size();
	for(int i=0;i<size;i++){
		if(!c[st[z][i]-'0']){
			dfs(st[z][i]);
		}
	}
}
int main(){
	cin>>a;
	cin>>k;
	for(int i=1;i<=k;i++){
	char o,p;
		cin>>o>>p;
		st[o].push_back(p);
	}
	//我根本就没有考虑到一个数字可以有多种变换 
	num[0]=1;
	int l=a.length();
	//接下来计算每一位的变换次数进行连乘 
	for(int i=0;i<l;i++){
		memset(c,0,sizeof(c));  //!!!!!写法每次循环都清空 
		dfs(a[i]);
		int summ=0;
		for(int j=0;j<=9;j++){
			summ+=c[j];      //summ表示这次变换得前面可以有多少种变化 
		}//高精度乘法 
		int x=0;
		for(int j=0;j<100;j++){
			num[j]=num[j]*summ+x;
			x=num[j]/10;
			num[j]%=10;
		}
	}
	int i=100;
	while(num[i]==0&&i>0) i--;
	for(;i>=0;i--) cout<<num[i];
return 0;
}

家庭问题(并查集的题目)

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<queue> 
#include<cstdio>
using namespace std;
//这个是并查集
//操作:合+并 
int father[101]; 
bool isroot[101];
int sum[101];
int n,k;
int findfather(int x){
	int a=x;
	while(x!=father[x]){
		x=father[x];
	}
	while(a!=father[a]){//路径压缩 
		int z=a;
		a=father[a];//把路径上的点的father都赋值为根节点 
		father[z]=x;
	}
	return x;
}

void Un(int a,int b){
	int faA=findfather(a);
	int faB=findfather(b);
	if(faA!=faB) father[faA]=faB;
}
void inti(int n){
	for(int i=1;i<=n;i++){
	
	father[i]=i;//初始化,每个都是独立的 
	isroot[i]=0;
}
}
int main(){
	cin>>n>>k;
	inti(n);
	int xx,yy;
	for(int i=1;i<=k;i++) {
		cin>>xx>>yy;
		Un(xx,yy);
	}
	for(int i=1;i<=n;i++){
		isroot[findfather(i)]=1;//是根节点的赋值为1 
		sum[findfather(i)]++;   //根节点的家庭成员++ 
	}
	int ans=0,maxn=-1;
	for(int i=1;i<=n;i++){
		 ans+=isroot[i];
		 maxn=max(maxn,sum[i]);
	}
	 cout<<ans<<" "<<maxn<<endl;
return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值