• HDU 2242 连通分量缩点+树形dp


    题目大意是:

    所有点在一个连通图上,希望去掉一条边得到两个连通图,且两个图上所有点的权值的差最小,如果没有割边,则输出impossible

    这道题需要先利用tarjan算法将在同一连通分量中的点缩成一个点后,重新构建一幅图,然后利用新建的图进行树形dp解决问题

    这道题目需要注意的是可能存在重边,那么子节点回到父节点有两条边及以上的话,就需要对子节点经过父节点的边进行low值更新

    tarjan算法学习的网站个人感觉还不错https://www.byvoid.com/blog/scc-tarjan/

      1 /*
      2 找割边是否存在
      3 */
      4 #include <cstdio>
      5 #include <cstring>
      6 #include <iostream>
      7 #include <stack>
      8 using namespace std;
      9 
     10 typedef long long ll;
     11 const int N = 10005;
     12 const int INF = 200000000;
     13 
     14 int first[N] , k , k_scc , val[N] , val_scc[N] , sum[N] , all ;
     15 int scc[N] , num_of_scc , dfs_clock , dfn[N] , low[N];
     16 stack<int> s;
     17 
     18 struct Edge{
     19     int x , y , next ;
     20     bool flag;
     21 }e[N<<2] , e_scc[N<<2];
     22 
     23 int my_abs(int x)
     24 {
     25     return x>=0?x:-x;
     26 }
     27 
     28 void add_edge(int x , int y)
     29 {
     30     e[k].x = x , e[k].y = y , e[k].next = first[x];
     31     first[x] = k++;
     32 }
     33 
     34 void add_edge_scc(int x , int y)
     35 {
     36     e_scc[k_scc].x = x , e_scc[k_scc].y = y , e_scc[k_scc].next = first[x] , e_scc[k_scc].flag = false;
     37     first[x] = k_scc++;
     38 }
     39 
     40 void tarjan(int u , int fa)
     41 {
     42     dfn[u] = low[u] = ++dfs_clock;
     43     s.push(u);
     44     int flag = 1; //用来解决重边情况
     45     for(int i=first[u] ; i!=-1 ; i=e[i].next){
     46         int v = e[i].y;
     47         /*
     48         因为第一次遇到父节点的边,表示是一开始下来的边,这个是不允许访问的,
     49         但是如果遇到2次及以上,说明u v之间不止一条边,访问到第二次之后的边是允许
     50         low[u]通过这个重边得到dfn[v]比较下的较小值进行更新
     51         */
     52         if(v == fa && flag){
     53             flag = 0;
     54             continue;
     55         }
     56         if(!dfn[v]){
     57             tarjan(v,u);
     58             low[u] = min(low[u] , low[v]);
     59         }
     60         else if(!scc[v])
     61             low[u] = min(low[u] , dfn[v]);
     62     }
     63     if(low[u] == dfn[u]){
     64         num_of_scc++;
     65         while(true){
     66             int x = s.top();
     67             s.pop();
     68             scc[x] = num_of_scc;
     69             val_scc[num_of_scc] += val[x];//得到新构建图上的点的权值
     70             if(x == u) break;
     71         }
     72     }
     73 }
     74 
     75 void dfs(int u , int fa)
     76 {
     77     sum[u] = val_scc[u];
     78     for(int i=first[u] ; i!=-1 ; i=e_scc[i].next){
     79         int v = e_scc[i].y;
     80         if(v == fa) continue;
     81         e_scc[i].flag = true;
     82         dfs(v , u);
     83         sum[u]+=sum[v];
     84     }
     85 }
     86 
     87 int main()
     88 {
     89   //  freopen("a.in" , "r" , stdin);
     90     int n , m , x , y;
     91     while(scanf("%d%d" , &n , &m) == 2){
     92         memset(first , -1 , sizeof(first));
     93         k=0 , all = 0;
     94         for(int i=0 ; i<n ; i++)
     95         {
     96             scanf("%d" , val+i);
     97             all += val[i];
     98         }
     99 
    100         for(int i=0 ; i<m ; i++){
    101             scanf("%d%d" , &x , &y);
    102             add_edge(x , y);
    103             add_edge(y , x);
    104         }
    105 
    106         //强连通分量缩点
    107         dfs_clock = 0 , num_of_scc = 0;
    108         memset(dfn , 0 ,sizeof(dfn));
    109         memset(scc , 0 , sizeof(scc));
    110         memset(val_scc , 0 , sizeof(val_scc));
    111         tarjan(0 , -1);
    112 
    113         if(num_of_scc == 1){
    114             puts("impossible");
    115             continue;
    116         }
    117 
    118         //重新构建一个以连通分量缩点后的树形图
    119         memset(first , -1 , sizeof(first));
    120         k_scc = 0;
    121         for(int i=0 ; i<k ; i++){
    122             if(scc[e[i].x] == scc[e[i].y]) continue;
    123             add_edge_scc(scc[e[i].x] , scc[e[i].y]);
    124         }
    125         dfs(1 , -1);
    126 
    127         int minn = INF;
    128         for(int i=0 ; i<k_scc ; i++){
    129             if(!e_scc[i].flag) continue;
    130             minn = min(minn , my_abs(all - sum[e_scc[i].y] - sum[e_scc[i].y]) );
    131         }
    132         printf("%d
    " , minn);
    133     }
    134     return 0;
    135 }
  • 相关阅读:
    Cocos2d-x教程(26)-Cocos2d-x + Lua脚本实现大地图缩放功能
    【C语言】不使用大小于号,求出两数最大值
    软件project—思考项目开发那些事(一)
    linux下误删数据文件恢复
    C++中stringstream ostringstream istringstream使用方式
    谈谈c++纯虚函数的意义!
    CountDownTimer完整具体演示样例
    Flex 正則表達式 电话、邮箱验证
    DIV+CSS两种盒子模型(W3C盒子与IE盒子)
    图论--欧拉路,欧拉回路(小结)
  • 原文地址:https://www.cnblogs.com/CSU3901130321/p/4234798.html
Copyright © 2020-2023  润新知