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这个数调换下顺序;就是