• Codeforces 1005F Berland and the Shortest Paths 【最短路树】【性质】


    其实是一道裸题,如果没学过最短路树的话会比较难做,要想很久想到关键性质才能做出来。

    最短路树顾名思义,就是从一个图中生成出来一棵树,使得每个顶点到root的距离是单源最短路。如果有这样的树的话,那可见这样的树是符合题意的。

    怎么生成这样的树呢?关键在于记录前驱father,一个距离root最短路是6的点必定从一个距离root最短路是5的点到达(这两个点之间一定会有一条边)。所以我们对于所有顶点 2-n,每个顶点u我们找dis[u] = dis[v]+1的情况,这样的话v就是u的前驱。若v,u之间有一条边,那u到root的最短路就解决了【因为如果v到root的最短路不变,那u也不变】,原问题就变成了子问题,这就是这么建树 正确性的理解。

    所有合法前驱记录完后,我们dfs下枚举所有前驱就可以了。【最多能生成father[2].size() * father[3].size() * ... * father[n].size()个合法答案】

     1 #include<iostream>
     2 #include<vector>
     3 #include<map>
     4 #include<cstring>
     5 #include<queue>
     6 using namespace std;
     7 
     8 char comp[200005];
     9 vector< pair<int,int> > edge[200005];
    10 vector<int> father[200005];
    11 vector<string> ans;
    12 int dis[200005],n,k;
    13 queue< pair<int,int> > q;
    14 
    15 void dfs(int u){
    16     if( ans.size()>=k ) return;
    17     if(u==n+1) { ans.push_back(comp+1); return;}//建完了 
    18     for(int i=0;i<father[u].size();i++){
    19         comp[ father[u][i] ] = '1';//从众多前驱中挑一个
    20         dfs(u+1);
    21         comp[ father[u][i] ] = '0'; 
    22     }
    23 }
    24 
    25 int main(){
    26     int m; cin>>n>>m>>k;
    27     for(int i=1;i<=m;i++){
    28         int u,v; scanf("%d %d",&u,&v);
    29         edge[u].push_back( make_pair(v,i) );
    30         edge[v].push_back( make_pair(u,i) );//建两条边 
    31     }
    32     
    33     
    34     memset(dis,-1,sizeof(dis));
    35     for (int i = 1; i <= m; i++) comp[i] = '0';
    36     //维护出dis数组
    37     q.push( make_pair(1,0) ); dis[1]=0;
    38     while(!q.empty()){
    39         pair<int,int> pa = q.front(); q.pop();
    40         int u=pa.first,d=pa.second;
    41         for(int i=0;i<edge[u].size();i++){
    42             int v=edge[u][i].first;
    43             if( dis[v]==-1 ) {
    44                 dis[v]=d+1;
    45                 q.push( make_pair(v,d+1) );
    46             } 
    47         }
    48     }
    49     //找最短路数里每个顶点的前驱 
    50     for(int i=2;i<=n;i++){
    51         for(int j=0;j<edge[i].size();j++){
    52             if( dis[i]==dis[ edge[i][j].first ]+1 ) father[i].push_back( edge[i][j].second );
    53         }
    54     }
    55     
    56     dfs(2);//从2开始建树 
    57     if(ans.size()>=k){
    58         cout<<k<<endl;
    59         for(int i=0;i<k;i++) cout<<ans[i]<<endl;
    60     }
    61     else{
    62         cout<<ans.size()<<endl;
    63         for(int i=0;i<ans.size();i++) cout<<ans[i]<<endl;
    64     }
    65     
    66     
    67     return 0;
    68 }
  • 相关阅读:
    IOS Application生命周期
    IOS ViewController 生命周期
    nsinteger 与 int 区别
    判断日期是昨天,今天,明天,后天,其他的显示星期
    改变图片颜色(灰色显示)
    Linux系统管理
    PHP 相关软件下载
    检查域名是否正确解析网站
    定时任务
    BZOJ 3534 [Sdoi2014]重建
  • 原文地址:https://www.cnblogs.com/ZhenghangHu/p/9315157.html
Copyright © 2020-2023  润新知