hdu 4912 Paths on the tree (LCA+贪心)

来源:互联网 发布:windows装linux双系统 编辑:程序博客网 时间:2024/05/09 19:35

题意:

一颗无根树上有很多条简单路径,已知这些简单路径的两个端点,求最多能选择多少条路径,

使得任意两条路径没有交点。(交点指此点既在路径a上,又在路径b上)。


算法:

怎样来确定路径呢?首先从端点u走到端点v必定要经过u和v的lca,即lca(u,v)必定在以u、v为端点

的简单路径上。那么我们可以通过每条路径端点的lca的深度从大到小排序。

为了选择的个数更多,就要尽量避免冲突,故利用贪心的思想,从lca大的开始选择,每次选择后,

把路径上的所有点都标记。这样下次就不会选择到一条有重复点的路径了。


关于lca有离线的tarjan算法(dfs+并查集) 和在线的基于dfs+ rmq的st算法 的倍增算法。

个人觉得lca离线算法比较好理解。


算法主体 

void tarjan(int x,int f){    fa[x] = x;    vis[x] = true;  //有时可以不要,直接用lev[]数组标记走没走过    for(int i=0;i<q[u].size();i++)    {        int v = q[u][i]; //询问LCA(u,v)        if(vis[x]) lca[u][v] = lca[v][u] = root(v); //root(v)即找v集合的根节点    }  //这里如果点的个数在100000的数量级或以上,二维的lca[][]是开不下的    //可以把第二个点v和询问的编号id作为pair放入q[u]数组中,这样就可以用lca[id] = root(v)记录了    for(int i=head[u];i!=-1;i=e[i].next)    {        int v = e[i].to;        if(v == f) continue;        if(!vis[v])        {            lev[v] = lev[u]+1;            tarjan(v);            fa[v] = u;  //将v并入u集合        }    }}


在线算法我学了一下午也没有特别深入的理解,我准备再学下rmq,这样再回头理解这个

LCA的在线算法。

我现在能理解的全部都注释到代码中了!


#include<cstdio>#include<iostream>#include<cstring>#include<vector>#include<algorithm>#include<queue>#define maxn 100010using namespace std;struct edge{    int to,next;}e[maxn<<1];struct node{    int u,v,lca;}t[maxn];int head[maxn],cnt,n;int lev[maxn],fa[maxn][20];bool vis[maxn];bool cmp(node x,node y){    return lev[x.lca]>lev[y.lca];}void init(){    memset(head,-1,sizeof(head));    cnt = 0;}void add(int x,int y){    e[cnt].to = y;    e[cnt].next = head[x];    head[x] = cnt++;}void bfs(int x){    queue<int> q;    lev[x] = 0;    fa[x][0] = x;    q.push(x);    while(!q.empty())    {        int u = q.front();        q.pop();        for(int i=1;i<20;i++)            fa[u][i] = fa[fa[u][i-1]][i-1];        for(int i=head[u];i!=-1;i=e[i].next)        {            int v = e[i].to;            if(v==fa[u][0]) continue;            fa[v][0] = u;            lev[v] = lev[u]+1;            q.push(v);        }    }}int LCA(int x,int y){    if(lev[x]<lev[y])  //如果x的深度比y的深度小则交换x和y    {        int tmp = x;        x = y;        y = tmp;    }    for(int i=19;i>=0;i--)    {        if((1<<i)&(lev[x]-lev[y]))  //如果x和y不在同一深度,x往上移动到他的祖先节点            x = fa[x][i];    }    if(x==y) return x;  //如果y在x到根节点之间的路径上,则lca(x,y) = y;    for(int i=19;i>=0;i--)    {        if(fa[x][i]!=fa[y][i]) //x和y到了同一深度后,继续往上移动到祖先节点        {                        //直到两个的祖先节点相同则找到lca            x = fa[x][i];            y = fa[y][i];        }    }    return fa[x][0];}void dfs(int u){    vis[u] = true;    for(int i=head[u];i!=-1;i=e[i].next)    {        int v = e[i].to;        if(vis[v] || lev[v]<lev[u]) continue;  //由于是无根树,所以只能把深度大于当前节点的子树中的点标记        dfs(v);    }}int main(){    //freopen("1.txt","r",stdin);    int m,a,b;    while(scanf("%d%d",&n,&m)!=EOF)    {        init();        for(int i=1;i<n;i++)        {            scanf("%d%d",&a,&b);            add(a,b);            add(b,a);        }        bfs(1);        for(int i=0;i<m;i++)        {            scanf("%d%d",&t[i].u,&t[i].v);            t[i].lca = LCA(t[i].u,t[i].v);        }        sort(t,t+m,cmp);        memset(vis,0,sizeof(vis));        int ans = 0;        for(int i=0;i<m;i++)        {            int xx = t[i].u,yy = t[i].v,zz = t[i].lca;            if(!vis[xx] && !vis[yy])            {                ans++;                dfs(zz);            }        }        printf("%d\n",ans);    }    return 0;}//在线算法 LCA倍增算法


离线算法如下:


#include<cstdio>#include<iostream>#include<cstring>#include<vector>#include<algorithm>#define maxn 100010using namespace std;struct edge{    int to,next;}e[maxn<<2];struct node{    int u,v,cs;}t[maxn];int cnt,head[maxn],lev[maxn],fa[maxn],n,lca[maxn];bool vis[maxn];vector<pair<int,int> > q[maxn];void init(){    for(int i=1;i<=n;i++)        q[i].clear();    cnt = 0;    memset(head,-1,sizeof(head));}void add(int x,int y){    e[cnt].to = y;    e[cnt].next = head[x];    head[x] = cnt++;}bool cmp(node x,node y){    return lev[x.cs]>lev[y.cs];}int root(int x){    if(fa[x]==x) return x;    else fa[x] = root(fa[x]);    return fa[x];}void dfs(int u,int f){    fa[u] = u;    pair<int,int> pp;    for(int i=0;i<q[u].size();i++)    {        pp = q[u][i];        int t1 = pp.first,t2 = pp.second;        if(lev[t1]==-1) continue;        t[t2].cs = root(t1);    }    for(int i=head[u];i!=-1;i=e[i].next)    {        int v = e[i].to;        if(v==f) continue;        lev[v] = lev[u]+1;        dfs(v,u);        fa[v] = u;    }}void dfs1(int x){    vis[x] = true;    for(int i=head[x];i!=-1;i=e[i].next)    {        int v = e[i].to;        if(vis[v] || lev[v]<lev[x]) continue;        dfs1(v);    }}int main(){    //freopen("1.txt","r",stdin);   // freopen("11.txt","w",stdout);    int m,a,b;    while(scanf("%d%d",&n,&m)!=EOF)    {        init();        for(int i=1;i<n;i++)        {            scanf("%d%d",&a,&b);            add(a,b);            add(b,a);        }        for(int i=0;i<m;i++)        {            scanf("%d%d",&a,&b);            q[a].push_back(make_pair(b,i));            q[b].push_back(make_pair(a,i));            t[i].u = a;            t[i].v = b;        }        memset(lev,-1,sizeof(lev));        lev[1] = 0;        dfs(1,-1);        sort(t,t+m,cmp);        int ans = 0;        memset(vis,0,sizeof(vis));        for(int i=0;i<m;i++)        {            int xx = t[i].u,yy = t[i].v,zz = t[i].cs;            if(!vis[xx] && !vis[yy])            {                ans++;                dfs1(zz);            }        }        printf("%d\n",ans);    }    return 0;}



0 0
原创粉丝点击