【UVA1267】Network

来源:互联网 发布:淘宝怎么寄到美国 编辑:程序博客网 时间:2024/06/03 19:53

题面

  Consider a tree network with n nodes where the internal nodes correspond to servers and the terminal nodes correspond to clients. The nodes are numbered from 1 to n. Among the servers, there is an original server S which provides VOD (Video On Demand) service. To ensure the quality of service for the clients, the distance from each client to the VOD server S should not exceed a certain value k. The distance from a node u to a node v in the tree is defined to be the number of edges on the path from u to v. If there is a nonempty subset C of clients such that the distance from each u in C to S is greater than k , then replicas of the VOD system have to be placed in some servers so that the distance from each client to the nearest VOD server (the original VOD system or its replica) is k or less.
  Given a tree network, a server S which has VOD system, and a positive integer k, find the minimum number of replicas necessary so that each client is within distance k from the nearest server which has the original VOD system or its replica.

题意

  有一棵nn1000)个节点的树,给定一个根S以及一个正整数K,现在根节点有一个标记,标记点能够覆盖距离不超过K的点,我们还要标记一些点,使得任意叶子节点都被覆盖(只有非叶子节点才能标记),问至少还要标记多少个点

解法

贪心:
  首先我们来证明一个结论:如果叶子节点u没有被覆盖,那么选择u的第K级父亲v进行标记最优(假设uv的子树内未被覆盖到的最深的叶子节点)
  其实这个结论挺显而易见的。如果选择v以上的点,那么其无法覆盖到u点,如果选择v的子树内的点x,那么因为uv的子树内最深的点,而v能覆盖u,那么x能够覆盖到的子树内的点,v也一样能够覆盖到;而对于子树外部的点y,因为从yx需要经过v,所以disxy肯定比disvy更大,所以v能覆盖的更远。得证。
  那么我们首先dfs一次,求出叶子节点,各节点的父亲,还有各节点的深度,将叶子节点丢进一个按深度降序排序的优先队列里。
  然后对根节点S进行拓展,将离S的距离为K以内的点进行覆盖,接着开始处理,取出队首元素u,如果u未被覆盖,找到uK级父亲v,对v进行拓展即可,复杂度O(Tn+qK),q为叶子节点个数

拓展

咖啡店:

分析

  这道题的暴力解法可以采用与上题类似的解法,区别在于这道题的根节点一开始没有被标记,其他过程则一致
  考虑正解:
  每个结点有一个深度,考虑深度最大的结点P,设它的父结点为P1P1的父结点为P2,依此类推。我们有以下引理 :
  引理1:在以Pk为根的子树中,至少要选择一个结点建立咖啡店:
  因为P距咖啡店的距离不能超过k,而所有与P距离不超过k的结点都在以Pk为根的子树,所以引理1成立。
  现在我们知道了一棵子树中至少有一个咖啡店 , 进一步 , 我们需要找到在哪个结点建咖啡店。
  引理2:在Pk结点上建立咖啡店不会比在其子孙结点上建咖啡店差,即我们之前证明了的结论
  这样 , 从最优的决策出发,我们必然要选择Pk建立咖啡店。
  我们现在会选第一个咖啡店了,接下来怎么选第二个咖啡店呢 ?
  很简单,我们已经确定好必然要选的一个咖啡店了,这个咖啡店的选择是与图的剩下部分无关的。这样,我们选择这个咖啡店,并把所有被它覆盖的结点打上被覆盖标记。接下来再选P为一个未标记的最深的结点,继续在它对应的Pk上建咖啡店。直到找不到P或找不到P对应的Pk了。
  若找不到P,则所有的结点都被覆盖,算法结束;若找不到P对应的Pk,则说明P到根结点的距离不超过k,可以选择根结点作为咖啡店,因为根结点可以覆盖所有当前没有被覆盖的结点。
  现在,我们已经找到了本题的一种解法,但用这个方法写程序会比较麻烦,比较需要对咖啡店附近的结点进行标记 , 怎样找一处有效的方法不使复杂度提高会出现一定的问题。而且程序实现还不够简单。
  利用上面的结论,我们换一种角度思考,对于一个结点 , 它可能是:
  (1) 咖啡店
  (2) 被它的子树中的一个咖啡店覆盖
  (3) 被一个不是它子树中的咖啡店覆盖
  我们从深度最大的结点开始处理。设当前处理的结点为P,则
  (1) 若P是咖啡店,则它的父结点P1被它覆盖,给父结点标记k1,表示父结点还能向周围覆盖k1层。
  (2) 若P被子孙结点覆盖,且有一个标记v,则当v>0时,其父结点P1同样要被覆盖,且能向周围覆盖v1层,给P1标记v1
  (3) 若P不会子孙结点覆盖,则它“将”被一个非子孙的结点覆盖。我们结它一个负数标号,其绝对值表示它的子树中最深的一个未被覆盖的结点距它多远。若一个结点的标记为v,则其父结点的标号设置为v1 。当值达到k 时 , 就必需要设置一个咖啡店了。
  这里 , 我们的给父结点标号的过程说得非常笼统,实际中并没有这么简单。因为一个结点可能有多个子结点,我们需要选择一个真正的标号。按如下方法选择 :
  (1) 对所有标号大于0的值,表示的是还能向上扩展多少层,当然是能扩展得越多越好,取最大值。
  (2) 对所有标号小于等于0的值 (包括从正数减到0的),表示下面有未被覆盖的最远距离,由于要全部覆盖,所以取最小值 ( 绝对值最大的 ) 。
  (3) 若一个结点两种类型都有,设正值为a,负值为b,则试着用覆盖此结点的咖啡店覆盖子树中的其他结点。只需要比较两个绝对值的大小即可。若a不小于b,则子树可用咖啡店覆盖,结点值设为a,否则最远的结点不能被覆盖,结点值设为b
  根据上面的描述,我们可以 : 依次给结点标记,在标记为k的地方建立咖啡店并改标记为k;最后,根结点标记若非正,则在其上建一个咖啡店。通过这样步骤 , 就可以找到咖啡店建在哪些地方。并找到最少需要建立多少个咖啡店。
  不难证明,上面两种方法是等价的,当然找到的咖啡店也是一样的。
  回顾第二种方法,我们所要做的是 : 首先对整棵树进行宽度优先遍历 , 找出处理的顺序。然后按宽度优先遍历的逆序遍历整棵树 , 同时建立标记和确实咖啡店的位置。都可以在O(n)的时间内完成。因此 , 算法的复杂度为O(n)
  咖啡店代码:https://gitee.com/qjlyh/codes/rq873peywsvdbu4jtg6ch96
  咖啡店数据:https://pan.baidu.com/s/1geX34rx

复杂度

O(Tn+qK

代码

#include<iostream>#include<cstdlib>#include<cstdio>#include<queue>#define Lint long long intusing namespace std;const int INF=1e9+7;const int MAXN=1010;struct node{    int next,to;}t[MAXN*2];bool vis[MAXN],cov[MAXN];int head[MAXN],num;int f[MAXN][12];int dep[MAXN];int T,n,s,lim;int ans;struct task{    int x;    bool operator < (const task &a) const    {        return dep[x]<dep[a.x];    }};priority_queue<task> q;void add(int u,int v){    t[++num]=(node){ head[u],v };    head[u]=num;}void init(){    for(int j=1;j<=10;j++)        for(int i=1;i<=n;i++)            f[i][j]=f[f[i][j-1]][j-1];}void Dfs(int k,int fa){    bool flag=1;    for(int i=head[k],x; i ;i=t[i].next)    {        x=t[i].to;        if( x==fa )   continue ;        f[x][0]=k,dep[x]=dep[k]+1;        Dfs( x,k ),flag=0;    }    if( flag )   q.push( (task){ k } );}void dfs(int k,int fa,int dep){    if( dep>lim )   return ;    cov[k]=1;    for(int i=head[k],x; i ;i=t[i].next)    {        x=t[i].to;        if( x==fa )   continue ;        dfs( x,k,dep+1 );    }}int kthfa(int x){    for(int i=10;i>=0;i--)        if( lim&(1<<i) )   x=f[x][i];    if( !x )   return s;    else   return x;}void work(){    dfs( s,0,0 );    int tmp;    while( !q.empty() )    {        tmp=q.top().x,q.pop();        if( cov[tmp] )   continue ;        dfs( kthfa( tmp ),0,0 );        ans++;    }}int main(){    int u,v;    scanf("%d",&T);    while( T-- )    {        ans=num=0;        scanf("%d",&n);        scanf("%d%d",&s,&lim);        for(int i=1;i<=n;i++)   head[i]=vis[i]=cov[i]=0;        for(int i=1;i<=n-1;i++)        {            scanf("%d%d",&u,&v);            add( u,v ),add( v,u );        }        Dfs( s,0 );        init();        work();        printf("%d\n",ans);    }    return 0;}
原创粉丝点击