zoukankan      html  css  js  c++  java
  • hdu 2242 考研路茫茫——空调教室

    双连通分量

    边双连通分量+DP  (其实不用DP,直接建树+遍历一次就能计算出全部的DP值)

    题意无向图连通,所以只要从一个点运行一次dfs即可,在运行dfs过程中保存下所有的桥并且计算出所有的边双连通分量。在tarjan之后对原图进行缩点,缩点后就能得到一棵,树边刚好就是全部的桥。缩点后每个大点都有一个权值,权值等于 = 属于该连通分量的每个小点的权值和。

    因此保存下全部桥是为了方便建树。建树之后对树进行一次遍历(很多人说是DP,其实不算是DP,只是简单的遍历而已)。遍历过程要计算每个节点的dp值,dp[i] = 以点i为节点的子树的所有节点的权值和 

    因此每计算完一个节点的dp值后,就可以看看切断这条树边,能不能更新最大的答案, 切断该边的结果为   (SUM - dp[i]) - dp[i]

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <utility>
    #include <vector>
    #include <stack>
    using namespace std;
    #define N 10010
    #define M 20010
    #define INF 0x3f3f3f3f
    
    int n,tot,val[N];
    int head[N],dfn[N],low[N],belong[N],ins[N],dcnt,bcnt;
    int weight[N],dp[N],vis[N],SUM,res;
    typedef pair<int ,int> pii;
    struct edge
    {
        int u,v,used,next;
    }e[2*M];
    vector<pii>bridge;
    stack<int>sta;
    vector<int>ver[N];
    
    void add(int u, int v, int k)
    {
        e[k].u = u; e[k].v = v; e[k].used = 0;
        e[k].next = head[u]; head[u] = k++;
        u = u^v; v = u^v; u = u^v;
        e[k].u = u; e[k].v = v; e[k].used = 0;
        e[k].next = head[u]; head[u] = k++;
    }
    
    void dfs(int u , int fa)
    {
        dfn[u] = low[u] = ++dcnt;
        sta.push(u); ins[u] = 1;
        for(int k=head[u]; k!=-1; k=e[k].next)
            if(!e[k].used)
            {
                e[k].used = e[k^1].used = 1;
                int v = e[k].v;
                if(!dfn[v]) //树边
                {
                    dfs(v,u);
                    low[u] = min(low[u] , low[v]);
                    if(low[v] > dfn[u]) //
                    {
                        bridge.push_back(make_pair(u,v));
                        while(true)
                        {
                            int x = sta.top();
                            sta.pop(); ins[x] = 0;
                            belong[x] = bcnt;
                            if(x == v) break;
                        }
                        bcnt++; //统计连通分支数
                    }
                }
                else if(ins[v]) //后向边
                    low[u] = min(low[u] , dfn[v]);
            }
    }
    
    inline int abs(int x ,int y)
    {
        return x>y? x-y : y-x ;
    }
    
    void travel(int u) //遍历整个树,顺便计算出dp值,并且顺便计算出答案
    {
        vis[u] = 1;
        dp[u] = weight[u];
        for(int i=0; i<ver[u].size(); i++)
        {
            int v = ver[u][i];
            if(vis[v]) continue;
            travel(v);
            dp[u] += dp[v]; //加上子树的dp值
        }
        res = min(res , abs(SUM-2*dp[u]));
    }
    
    void build() //缩点后建树,树边就是桥
    {
        SUM = 0;
        res = INF;
        for(int i=0; i<bcnt; i++)
        {
            weight[i] = vis[i] = dp[i] = 0;
            ver[i].clear();
        }
        //计算缩点后每个点的权值
        for(int i=0; i<n; i++)
            weight[belong[i]] += val[i];
        for(int i=0; i<bcnt; i++)
            SUM += weight[i];
    
        for(int i=0; i<bridge.size(); i++)
        {
            int u = belong[ bridge[i].first ];
            int v = belong[ bridge[i].second ];
            ver[u].push_back(v);
            ver[v].push_back(u);
        }
        travel(0);
    //    for(int i=0; i<n; i++) printf("%d[%d]\n",i,belong[i]);
    //    for(int i=0; i<bcnt; i++) printf("w=%d\n",weight[i]);
    //    for(int i=0; i<bcnt; i++) printf("dp=%d\n",dp[i]);
    }
    
    void solve()
    {
        dcnt = bcnt = 0;
        bridge.clear();
        while(!sta.empty()) sta.pop();
        memset(ins,0,sizeof(ins));
        memset(dfn,0,sizeof(dfn));
        dfs(0,-1);
        while(!sta.empty())
        {
            int x = sta.top();
            sta.pop(); ins[x] = 0;
            belong[x] = bcnt;
        }
        bcnt++;
    
        if(bcnt == 1) //整个图就是个边双连通分支,不存在桥
        {
            cout << "impossible" << endl;
            return ;
        }
        
        build(); //建树,并且遍历,遍历过程中就能计算出答案
        cout << res << endl;
    }
    
    int main()
    {
        while(cin >> n >> tot)
        {
            tot *= 2;
            memset(head,-1,sizeof(head));
            for(int i=0; i<n; i++) cin >> val[i];
            for(int i=0; i<tot; i+=2)
            {
                int u,v;
                cin >> u >> v;
                add(u,v,i);
            }
            solve();
        }
        return 0;
    }
  • 相关阅读:
    Render Props
    react16新特性
    typescript
    calc
    类数组
    promise fullfill状态时 value是一个promise,那么此promise.then()里面收到的是什么
    M个同样的苹果放N个同样的盘子,允许有盘子空着, 问有多少种放法?
    history
    js创建二维数组
    钉钉-E应用开发初体验(企业内部应用)
  • 原文地址:https://www.cnblogs.com/scau20110726/p/3087018.html
Copyright © 2011-2022 走看看