搜索基础

为了方便复习整理(凑博客数),决定把之前那篇《算法竞赛进阶指南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出发可以到达的所有节点的集合 f(x) f ( x ) ,则我们在更新答案是相当在求 f(x)f(y) f ( x ) ⋃ f ( y ) (其中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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值