• 【例题收藏】◇例题·II◇ Berland and the Shortest Paths


    ◇例题·II◇ Berland and the Shortest Paths

    题目来源:Codeforce 1005F +传送门+


    ◆ 简单题意

    给定一个n个点、m条边的无向图。保证图是连通的,且m≥n-1。

    以首都(1节点)为根节点生成最小树。树的值定义为每个节点的深度和(根节点深度0)。举个例子:

    而我们知道,可能有多种情况使树的权值最小,题目给出了一个整数k,如果最小树的生成方案数为ans,当 ans≤k 时,将 ans 种方案全部输出;当 ans>k 时,任意输出 k 种不同生成方案即可。输出方案格式为一个01串,第i个字符如果为0,表示不选第i条边(按照输入顺序),1为选择第i条边。


    ◆ 解析

    其实点 i 的深度 dep[i] 就是根节点1到 i 的路径,而我们知道 1 到 i 没有任何一条路径短于它们的最短路径,所以生成树的权值最小时,根节点到每个点的距离就是原图中根节点到每个节点的最短路径。也就是说,我们生成的最小树就是一个最短路径树。然而显然有时候存在多条最短路径,这也就造成了我们生成的最小树有多种解。于是我们假装生成一棵树,实际上只是生成一个图。

    由于这道题的边权都是等价的(不如就把边权看成1吧),我们可以用BFS直接求得最短路,所以说其实这也是一个BFS序图。为了考虑每种情况,我们把所有最小的BFS序边连上。下面再举一个生成BFS序图的例子(希望入门reader可以理解):

    这样我们就生成了一个BFS序有根图,由于我们要生成树,而树的每一个节点的父节点少于一个。在上图中,4的父节点有两个,因此需要断开一条边——两条边是等价的,断掉任意一条即可。

    我们可以把 u→v 的边存入v的边集 min_edg[v] ,那么最小权值树则是对于每一个除根节点之外的 v,选择 min_edg[v] 中的任意一条边,所以方案总数为 (除去根节点 i:2~n)min_edg[v]的边数之积。最后再DFS递归求方案即可(具体见代码)。


    ◆ 源代码

     1 /*Lucky_Glass*/
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<vector>
     6 #include<queue>
     7 using namespace std;
     8 const int MAXN=int(2e5);
     9 const int INF=int(1e9);
    10 int n_pnt,n_edg,k;
    11 int dis[MAXN+5];
    12 vector<pair<int,int> > lnk[MAXN+5];
    13 vector<pair<int,int> > min_edg[MAXN+5];
    14 void BFS(int start)
    15 {
    16     fill(dis,dis+MAXN+5,INF);
    17     dis[start]=0;
    18     queue<int> que;
    19     que.push(start);
    20     while(!que.empty())
    21     {
    22         int u=que.front();que.pop();
    23         for(int i=0;i<lnk[u].size();i++)
    24         {
    25             int v=lnk[u][i].first,id=lnk[u][i].second,Stp=dis[u]+1;
    26             if(Stp>dis[v]) continue;
    27             min_edg[v].push_back(make_pair(u,id));
    28             if(Stp!=dis[v])
    29                 dis[v]=Stp,que.push(v);
    30         }
    31     }
    32 }
    33 bool chose[MAXN+5];int cnt;
    34 void DFS(int v)
    35 {
    36     if(v==n_pnt+1)
    37     {
    38         cnt++;
    39         for(int i=1;i<=n_edg;i++)
    40             printf("%d",chose[i]);
    41         printf("
    ");
    42         return;
    43     }
    44     for(int i=0;i<min_edg[v].size();i++)
    45     {
    46         chose[min_edg[v][i].second]=true;
    47         DFS(v+1);
    48         chose[min_edg[v][i].second]=false;
    49         if(cnt==k) return;
    50     }
    51 }
    52 int main()
    53 {
    54     scanf("%d%d%d",&n_pnt,&n_edg,&k);
    55     for(int i=0,u,v;i<n_edg;i++)
    56         scanf("%d%d",&u,&v),
    57         lnk[u].push_back(make_pair(v,i+1)),
    58         lnk[v].push_back(make_pair(u,i+1));
    59     BFS(1);
    60     long long ans=1;
    61     for(int i=2;i<=n_pnt;i++)
    62     {
    63         ans*=min_edg[i].size();
    64         if(ans>k) break;
    65     }
    66     printf("%lld
    ",min(k*1ll,ans));
    67     DFS(2);
    68     return 0;
    69 }

    The End

    Thanks for reading!

    - Lucky_Glass

    (Tab:如果我有没讲清楚的地方可以直接在邮箱lucky_glass@foxmail.com email我,在周末我会尽量解答并完善博客~)

  • 相关阅读:
    XML属性
    4.9Java游戏项目练习
    关于JVM结构的学习
    HelloWorld之Struts2
    进程调度
    JVM垃圾回收总结
    学会阅读Java字节码
    关于产品需求文档的各种D
    刘强东学习亚马逊:控制供应链 技术是最大障碍
    JVM内存溢出的方式
  • 原文地址:https://www.cnblogs.com/LuckyGlass-blog/p/9338902.html
Copyright © 2020-2023  润新知