【图的dfs + bitset+ 套路】hihocoder. 1041

http://hihocoder.com/problemset/problem/1041

每当我以为 dfs 我学得还可以了,就会发现自己其实对某些性质运用的并不是那么熟练
(向黑恶势力低头

其实 这题考点主要是图的dfs ,但是中间的某个步骤可以用bitset 优化一下;
题意:
给一颗树, 每条边 可以走两次,来回各一次。 给你一个遍历顺序,问是否能够按照顺序遍历。
比如:
7
1 2
1 3
2 4
2 5
3 6
3 7

如果按 3 7 2 遍历 可行, 但是按照 3 2 7 遍历则不可以,因为边只能走两次;
然后因为这题 n 比较小,所以可以强行暴力 硬A 过去。
这里讲一个O n 时间复杂度的方法,利用的就是dfs的性质

先说一个方法:

void dfs(int rot,int pre){
    vis[rot][rot]=1;
    for(int i=head[rot];i!=-1;i=edge[i].next){
        int v=edge[i].to;
        if(v==pre) continue;
        dfs(v,rot);
        for(int j=1;j<=n;j++){
            vis[rot][j] |=vis[v][j];
        }
    }
}

看看这个小套路 利用dfs 小性质 来完成 某些点到某些点的子树。
但这里显然是int vis[N][N] 来完成的, 不但可能开不下,而且这里还是n*n 的复杂度,谈何On 呢
这里显然就要用到 bitset,

这里 插入学习一下bitset。
可以当作一个bool型数组考虑,
bitset< N> bs; 可以考虑成一个数组bool bs[N]。

相关操作:
bs.set(); 全部置1,bs.reset()全部置0;
bs.set(pos);等价于bs[pos]=1,bs.reset(pos)等价于bs[pos]=0;

最重点的来了,bitset< N> a, b;

a和b可以直接进行操作,
!a //按位取反
a^b //按位异或
a|b //按位或
a&b //按位与
a=b<<3 //整体移位
a.count(); //a中1的个数

所以上面那段代码

void dfs(int rot,int pre){
    vis[rot][rot]=1;
    for(int i=head[rot];i!=-1;i=edge[i].next){
        int v=edge[i].to;
        if(v==pre) continue;
        dfs(v,rot);
        vis[rot]|=vis[v];  //因为二进制运算 这里的时间复杂度 就是常数级别
    }
}

然后我们知道了这个
接下来的处理方式 就仁者见仁了。

我们可以这样

void dfs2(int rot,int pre){
    if(flag) return;
    if(pos==m){
        flag=1;
        return;
    }
    if(rot==f[pos])
        pos++;  // 走下个点

    num[rot]++;  // num这点走了一次
    while(pos<=m){
        int cnt=pos;
        for(int i=head[rot];i!=-1;i=edge[i].next){
           int v=edge[i].to;
           if(v==pre || num[v]) continue; //若一条路一个方向只能走一次
           if(vis[v][f[pos]]){
               dfs2(v,rot);
            }
        }
       if(cnt==pos)
           break;
    }

}

这个的精髓就在于利用的dfs 的回溯走了那一遍回来的路。
比如 3 7 2
1走到3 之后 又走到7 之后 回溯到1 又走到 2

327 的话 走完2之后 就走不了3 -7 ,因为3走过一遍。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<stdlib.h>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<bitset>
#define mem(a) memset(a,0,sizeof(a))
#define ll __int64
#define INF 0x7fffffff   //INT_MAX
#define inf 0x3f3f3f3f   //
const double PI = acos(-1.0);
const double e = exp(1.0);
template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }
using namespace std;
/*
 * 学习一下bitset。
可以当作一个bool型数组考虑,
bitset<N> bs;  可以考虑成一个数组bool bs[N]。

相关操作:
bs.set(); 全部置1,bs.reset()全部置0;

bs.set(pos);等价于bs[pos]=1,bs.reset(pos)等价于bs[pos]=0;

最重点的来了,bitset<N> a, b;

a和b可以直接进行操作,
!a //按位取反
a^b //按位异或
a|b //按位或
a&b //按位与
a=b<<3 //整体移位
a.count(); //a中1的个数

*/
//int vis[105][105];
bitset<105>vis[105];

struct Edge{
    int to;
    int next;
    int use;
}edge[1000];
int cnt;
int head[1005];
int n,m;
void addedge(int u,int v){
    edge[cnt].to=v;
    edge[cnt].use=0;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
void dfs(int rot,int pre){
    vis[rot][rot]=1;
    for(int i=head[rot];i!=-1;i=edge[i].next){
        int v=edge[i].to;
        if(v==pre) continue;
        dfs(v,rot);
        vis[rot]|=vis[v];
    }
}
int flag;
int pos;
int f[105];
int num[105];
void dfs2(int rot,int pre){
    if(flag) return;
    if(pos==m){
        flag=1;
        return;
    }
    if(rot==f[pos])
        pos++;  // 走下个点

    num[rot]++;  // num这点走了一次
    while(pos<=m){
        int cnt=pos;
        for(int i=head[rot];i!=-1;i=edge[i].next){
           int v=edge[i].to;
           if(v==pre || num[v]) continue; //若一条路一个方向只能走一次
           if(vis[v][f[pos]]){
               dfs2(v,rot);
            }
        }
       if(cnt==pos)
           break;
    }

}
void init(){

    for(int i=1;i<=n;i++)
        vis[i].reset();
    memset(num,0,sizeof(num));
    cnt=0;
    memset(head,-1,sizeof(head));
}
int main() {
    freopen("1.txt","r",stdin);
    int cas;
    scanf("%d",&cas);
    while(cas--){
        init();
        scanf("%d",&n);
        for(int i=1;i<n;i++){
            int u,v;
            scanf("%d %d",&u,&v);
            addedge(u,v);
            addedge(v,u);
        }
        dfs(1,0);
        scanf("%d",&m);
        for(int i=1;i<=m;i++){
            scanf("%d",&f[i]);
        }
        flag=0;
        pos=1;
        dfs2(1,-1);
        flag?printf("YES\n"):printf("NO\n");
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值