您的位置:首页 > 其它

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

2014-08-11 16:17 337 查看
题意:

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

使得任意两条路径没有交点。(交点指此点既在路径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 100010

using 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 100010

using 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: