22.3.11


1,天体赛的善良(桶排序思想?)2,清点代码库(结构体排序-新用法!!)3,bfs走迷宫


1,L1天梯赛的善良

就是给你最多1e6个数,让你输出最小值及个数,最大值及个数;

正常排序只能18分?我是交了几次都是18分

看另一个人的做法,就是cin>>x;a[x]++,是的,将大小映射到数组的下标!这个思想很巧;

输出最小值及个数时,只需要正序遍历a数组,碰到第一个a[i]!=0,即是最小值,i是最小值,a[i]是个数,输出后break就行;最大值则倒序遍历即可;

#include<bits/stdc++.h>
#define rep1(i,a,n) for(int i=a;i<n;i++) 
#define rep2(i,a,n) for(int i=a;i<=n;i++) 
#define per1(i,n,a) for(int i=n;i>a;i--) 
#define per2(i,n,a) for(int i=n;i>=a;i--)
typedef long long ll;
using namespace std;
void quick_cin()
{
	cin.tie(0);
	ios::sync_with_stdio(false);
}
int n,m;
const int N=2e4+10;
int a[N];
int main()
{
	quick_cin();
	cin>>n;
	rep2(i,1,n)
	{
		int x;
		cin>>x;
		a[x]++;
	}
	rep2(i,0,N)
	{
		if(a[i]!=0)
		{
			cout<<i<<" "<<a[i]<<endl;
			break;
		}
	}
	per2(i,N,1)
	{
		if(a[i]!=0)
		{
			cout<<i<<" "<<a[i];
			break;
		}
	}
	return 0;
}

2,清点代码库

新的结构体排序,解决有多重排序需求的任务;

例如给出总分和语文分,按总分降序输出,若 总分相同,按语文分升序输出;

a是语文分,sum是总分;

bool cmp(ji a,ji b)
{
	if(a.sum==b.sum)
	{
		return a.a<b.a;
	}
	else
	{
		return a.sum>b.sum;
	}
}

3,bfs

队列实现过程:第一个访问的点入队列;

删除第一个 点,第一个点旁未访问的邻接点2,3入队列;

删除邻接点2,2的邻接点入队;

删除邻接点3,3的邻接点入队;

一直到最后队列为空;

由此发现,这是一层一层访问的;每一次访问,距离都会+1;

#include<bits/stdc++.h>
#define rep1(i,a,n) for(int i=a;i<n;i++) 
#define rep2(i,a,n) for(int i=a;i<=n;i++) 
#define per1(i,n,a) for(int i=n;i>a;i--) 
#define per2(i,n,a) for(int i=n;i>=a;i--)
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
void quick_cin()
{
	cin.tie(0);
	ios::sync_with_stdio(false);
}
const int N=1e3;
int n,m;
int a[N][N],d[N][N];
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
int bfs()
{	
	queue<PII>q;
	memset(d,-1,sizeof d);
	d[0][0]=0;
	q.push({0,0});
	while(q.size())
	{
		auto t=q.front();
		q.pop();//一定不要忘了删除!!
		rep2(i,0,3)
		{
			int x=t.first+dx[i],y=t.second+dy[i];
			if(x>=0&&x<n&&y>=0&&y<m&&a[x][y]==0&&d[x][y]==-1)
			{
				d[x][y]=d[t.first][t.second]+1;
				q.push({x,y});
			}
		}
	}
	return d[n-1][m-1];
}
int main()
{
	quick_cin();
	cin>>n>>m;
	rep2(i,0,n-1)
	rep2(j,0,m-1)cin>>a[i][j];
	cout<<bfs()<<endl;
	return 0;
}

4,八数码

难点,如何表示当前状态,及转移后的状态;

技巧:①一维数组的下标k,转换为二维3x3的下标,x=k/3  ,y=k%3;常用,记住!

②那么已知x,y如何在转化回去k呢, x*3+b;

注意,从0行0列开始适用,否则要-1;

③string +号表示连接字符串的意思;

注意理解交换后,在交换回来;

#include<bits/stdc++.h>
#define rep1(i,a,n) for(int i=a;i<n;i++) 
#define rep2(i,a,n) for(int i=a;i<=n;i++) 
#define per1(i,n,a) for(int i=n;i>a;i--) 
#define per2(i,n,a) for(int i=n;i>=a;i--)
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
void quick_cin()
{
	cin.tie(0);
	ios::sync_with_stdio(false);
}
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
int bfs(string s)
{	
	queue<string>q;
	q.push(s);
	unordered_map<string,int>d;
	string end="12345678x";
	while(q.size())
	{	
		auto t=q.front();
		q.pop();
		if(t==end)return d[t];
		int dis=d[t];
		int k=t.find('x');
		int x=k/3,y=k%3;
		rep2(i,0,3)
		{
			int a=x+dx[i],b=y+dy[i];
			if(a>=0&&a<3&&b>=0&&b<3)
			{
				swap(t[a*3+b],t[k]);
				if(!d.count(t))
				{
					q.push(t);
					d[t]=dis+1;
				}
				swap(t[a*3+b],t[k]);				
			}
		}
	}
	return -1;
}
int main()
{
	quick_cin();
	string s;
	rep2(i,0,8)
	{
		char c;
		cin>>c;
		s+=c;
	}
	cout<<bfs(s)<<endl;

	return 0;
}

4,树的dfs

例题:树的中心;

①建立邻接表;

int h[N], e[N * 2], ne[N * 2], idx;

void add(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

 ②树的bfs模板

// 需要标记数组st[N],  遍历节点的每个相邻的点
void dfs(int u) {
    st[u] = true; // 标记一下,记录为已经被搜索过了,下面进行搜索过程
    for (int i = h[u]; i != -1; i = ne[i]) {
        int j = e[i];
        if (!st[j]) {
            dfs(j);
        }
    }
}

本题的本质是树的dfs, 每次dfs可以确定以u为重心的最大连通块的节点数,并且更新一下ans。

也就是说,dfs并不直接返回答案,而是在每次更新中迭代一次答案。

#include<bits/stdc++.h>
#define rep1(i,a,n) for(int i=a;i<n;i++) 
#define rep2(i,a,n) for(int i=a;i<=n;i++) 
#define per1(i,n,a) for(int i=n;i>a;i--) 
#define per2(i,n,a) for(int i=n;i>=a;i--)
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
void quick_cin()
{
	cin.tie(0);
	ios::sync_with_stdio(false);
}
const int N=1e5+10;
const int M=N*2;
int h[N],e[M],ne[M],idx,n,ans=N;
//e[]存储元素,ne[]存储next值,h[]存储每个树的头结点
bool st[N];
void add(int a,int b)
{
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int dfs(int u)
{
	int res=0;//存储 删掉某个节点之后,最大的连通子图节点数
	st[u]=1;
	int sum=1; //存储 以u为根的树 的节点数, 包括u,如图中的4号节点
	for(int i=h[u];i!=-1;i=ne[i])
	{
		int j=e[i];
		if(!st[j])
		{
			int s=dfs(j);
			res=max(res,s);
			sum+=s;
		}
	}
	res=max(res,n-sum);// 选择u节点为重心,最大的 连通子图节点数
	ans=min(res,ans);//遍历过的假设重心中,最小的最大联通子图的 节点数
	return sum;
}
int main()
{
	quick_cin();
	cin>>n;
	memset(h,-1,sizeof h);//初始化h数组 -1表示尾节点
	rep2(i,1,n-1)
	{
		int a,b;
		cin>>a>>b;
		add(a,b),add(b,a);
	}
	dfs(1);//可以任意选定一个节点开始 u<=n
	printf("%d",ans);
	return 0;
}

 5,递归搜索树;

此题可以考虑dfs做数字排列那个,就是输出方式的判断变了;

都是递归回溯,递归回溯;从左到右,走这么一个递归搜索树;

对于每个数字,都有选与不选两种状态; 

输出顺序呢就是由左及右; 

#include<bits/stdc++.h>
#define rep1(i,a,n) for(int i=a;i<n;i++) 
#define rep2(i,a,n) for(int i=a;i<=n;i++) 
#define per1(i,n,a) for(int i=n;i>a;i--) 
#define per2(i,n,a) for(int i=n;i>=a;i--)
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
void quick_cin()
{
	cin.tie(0);
	ios::sync_with_stdio(false);
}
	int n;
const int N=20;
bool st[N];
void dfs(int u)
{
	if(u>n)
	{
		rep2(i,1,n)
		{
			if(st[i])printf("%d ",i);
		}
		puts("");
		return ;
	}
	st[u]=1;
	dfs(u+1);
	st[u]=0;
	dfs(u+1);
}
int main()
{
	quick_cin();
	cin>>n;
	dfs(1);
	return 0;
}

当然了,这个题还有个优化,状态表示用状态压缩来优化;

#include<bits/stdc++.h>
#define rep1(i,a,n) for(int i=a;i<n;i++) 
#define rep2(i,a,n) for(int i=a;i<=n;i++) 
#define per1(i,n,a) for(int i=n;i>a;i--) 
#define per2(i,n,a) for(int i=n;i>=a;i--)
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
void quick_cin()
{
	cin.tie(0);
	ios::sync_with_stdio(false);
}
int n;

// u是当前枚举到的数,state是二进制数记录哪些数被选
void dfs(int u, int state) {
    if (u == n) {
        for (int i = 0; i < n; i ++)
            if (state >> i & 1)
                cout << i + 1 << " ";
            cout << endl;
        return ;
    }

    dfs (u + 1, state);  // 不用u这个数
    dfs (u + 1, state | (1 << u)); // 用u这个数
}

int main()
{
	quick_cin();
    cin >> n;
    dfs(0, 0);
    return 0;
}

这个题的遍历顺序和dfs选和不选的顺序有关,上面的代码呢,就是

当把不用 u这个数,和用u这个数调换下顺序;就是

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dull丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值