为了方便复习整理(凑博客数),决定把之前那篇《算法竞赛进阶指南0x20搜索》分开小节记录博客。这一节记录一些搜索的入门基础题目。
0x21 树和图的遍历:
先放一点基本的代码
void ins(int x,int y)
{
len++; a[len].x=x;a[len].y=y;
a[len].next=last[x];last[x]=len;
}
//图的遍历
void dfs(int x)
{
v[x]=1;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(v[y]) continue;
dfs(y);
}
}
//dfs序
void dfs(int x)
{
dfn[++m]=x;
v[x]=1;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(v[y]) continue;
dfs(y);
}
a[++m]=x;
}
//树的深度
void dfs(int x)
{
v[x]=1;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(v[y]) continue;
dep[y]=dep[x]+1;
dfs(y);
}
}
//树的重心
void dfs(int x)
{
v[x]=1; size[x]=1;
int max_part=0;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(v[y]) continue;
size[x]+=size[y];
max_part=max(max_part,size[y]);
}
max_part=max(max_part,n-size[x]);
if(max_part<ans)
{
ans=max_part;
pos=x;
}
}
//图的连通块
void dfs(int x)
{
v[x]=cnt;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(v[y]) continue;
dfs(y);
}
}
for(int i=1;i<=n;i++)
{
if(!v[i])
{
cnt++;
dfs(i);
}
}
//图的bfs遍历
void bfs()
{
memset(dep,0,sizeof(dep));
queue<int> q;
q.push(1); d[1]=1;
while(q.size())
{
int x=q.front; q.pop();
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
dep[y]=dep[x]+1;
q.push(y);
}
}
}
//拓扑排序
void topsort()
{
queue<int> q;
for(int i=1;i<=n;i++)
if(ru[i]==0)
q.push(i);
while(q.size())
{
int x=q.front(); q.pop();
tops[++cnt]=x;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
ru[y]--; if(ru[y]==0) q.push(y);
}
}
}
【例题】可达性统计
给定一张N个点M条边的有向无环图,分别统计从每个点出发能够到达的点的数量。N,M≤30000。
题解:
因为边是有向边,那么:从x出发可以到达的所有点=x的所有后继节点出发可以到达的所有点之和。 因为我们要从x的后继来更新x所以我们可以将图反建,用拓扑排序分层遍历,在遍历的过程中更新答案。
更新答案可以记录从x出发可以到达的所有节点的集合 ,则我们在更新答案是相当在求 (其中y是x的后继),我们可以用一个二进制储存这个函数值,第i位表示x能到达i,那么我们求并集的操作相当于位运算中的或操作,最终每个f(x)中1的个数就是答案。 过程可以用bitset实现。
code:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<bitset>
using namespace std;
const int N=30010;
bitset<N> b[N];
struct node
{
int x,y,next;
}a[N*4]; int len,last[N];
int cnt;
int n,m;
void ins(int x,int y)
{
a[++len].x=x;a[len].y=y;
a[len].next=last[x];last[x]=len;
}
int tops[N]; int ru[N];
void topsort()
{
queue<int> q;
for(int i=1;i<=n;i++)
{
b[i][i]=1;
if(ru[i]==0)
q.push(i);
}
while(q.size())
{
int x=q.front(); q.pop();
tops[++cnt]=x;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
ru[y]--; b[y]|=b[x];
if(ru[y]==0) q.push(y);
}
}
}
int main()
{
scanf("%d%d",&n,&m);
len=0; memset(last,0,sizeof(last));
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
ins(y,x); ru[x]++;
}
topsort();
for(int i=1;i<=n;i++) printf("%d\n",b[i].count());
return 0;
}
【例题】小猫爬山
Freda和rainbow让N只小猫坐索道下山。索道上的缆车最大承重量为W,而N只小猫的重量分别是C1、C2……CN。当然,每辆缆车上的小猫的重量之和不能超过W。每租用一辆缆车,Freda和rainbow就要付1美元,所以他们想知道,最少需要付多少美元才能把这N只小猫都运送下山?
题解:
爆搜,没什么好说的。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=20;
const int INF=999999999;
int n,w,minn;
int s[N],c[N];
void dfs(int now,int cnt)
{
if(cnt>=minn) return;
if(now==n+1)
{
minn=min(minn,cnt);
return;
}
for(int i=1;i<=cnt;i++)
{
if(s[i]+c[now]<=w)
{
s[i]+=c[now];
dfs(now+1,cnt);
s[i]-=c[now];
}
}
s[cnt+1]=c[now];
dfs(now+1,cnt+1);
s[cnt+1]=0;
}
int main()
{
scanf("%d%d",&n,&w);
for(int i=1;i<=n;i++) scanf("%d",&c[i]);
minn=n;
dfs(1,0);
printf("%d\n",minn);
return 0;
}