程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> poj 2822 & hdu 4280 <平面圖網絡流>

poj 2822 & hdu 4280 <平面圖網絡流>

編輯:C++入門知識

對於10^5個點10^6條邊的網絡流,一般做法無法高效解決,如果所有邊都是雙向邊且網絡能構成一個平面圖,則可以通過求解對偶圖的最短路求解,復雜度為O(M*log(M)),轉化方法類似於《平面圖S-T最小割》, 與S-T最小割平面圖較規則不同,難點在於將一張圖的塊求出。大體分如下幾步進行:
①把所有的邊都拆成兩條有向邊,自環刪掉。
②將每條有向邊在另一個圖G‘中用一個點表示。
③考察原圖中的每個頂點,將所有的與之相連的邊極角排序。
④遍歷每條入邊。將其後繼設為與之順時針相鄰的出邊。也就是在G’中連一條從這個入邊的點到其後繼的有向邊。
###注意(S, T)的那條新加邊要特殊處理。
⑤在G'中就是一些不相交的有向環。每個有向環就對應一個區域。找出了所有的區域,我們要的那張圖就簡單了。
⑥根據對偶圖構圖,求得s-t之間最短路即是對應的最小割
###至於“死胡同”問題(構不成平面的邊)這樣會形成一個特殊的區域,相當於進去死胡同再出來。但是答案不會受到影響,所以直接忽略。
 hdu 4280 Island Transport代碼:
[cpp] 
#pragma comment(linker, "/STACK:16777216") 
#include <cmath> 
#include <cstdio> 
#include <cstdlib> 
#include <cstring> 
#include <algorithm> 
#include <vector> 
#include <set> 
using namespace std; 
const int maxn = 100010; 
const int maxm = 100010; 
const double inf = 1 << 28; 
struct node { 
    int be, ne; 
    double val; 
    void init(int b, int e, double v) { 
        be = b; 
        ne = e; 
        val = v; 
    } 
}; 
 
int cmp(double a, double b) { 
    double eps = 1e-8; 
    if (a - b > eps) 
        return 1; 
    else if (a - b >= -eps) 
        return 0; 
    else 
        return -1; 

struct SPFA { 
    node buf[maxm * 2]; 
    int len, E[maxn], queue[maxn]; 
    double d[maxn]; 
    void init() { 
        memset(E, -1, sizeof(E)); 
        len = 0; 
    } 
    void add(int a, int b, double v) { 
        if (a == b) 
            return; 
        buf[len].init(b, E[a], v); 
        E[a] = len++; 
    } 
    int vis[maxn]; 
    double solve(int s, int t) { 
        int head = 0, tail = 0; 
        memset(vis, 0, sizeof(vis)); 
        memset(d, 255, sizeof(d)); 
        d[s] = 0; 
        queue[(tail++) % maxn] = s; 
        vis[s] = true; 
        int a, b; 
        while (head != tail) { 
            a = queue[(head++) % maxn]; 
            vis[a] = 0; 
            for (int i = E[a]; i != -1; i = buf[i].ne) { 
                b = buf[i].be; 
                if (cmp(d[a] + buf[i].val, d[b]) == -1) { 
                    d[b] = d[a] + buf[i].val; 
                    if (!vis[b]) { 
                        vis[b] = 1; 
                        queue[(tail++) % maxn] = b; 
                    } 
                } 
            } 
        } 
        return d[t]; 
    } 
} sp; 
struct arch { 
    int in, out; 
    double angle; 
    arch(int a, int b, double c) { 
        in = a; 
        out = b; 
        angle = c; 
    } 
    bool operator <(const arch& oth) const { 
        return cmp(angle, oth.angle) == -1; 
    } 
}; 
int n, m; 
double px[maxn], py[maxn], cap[maxm]; 
vector<arch> vertex[maxn]; 
void init() { 
    scanf("%d%d", &n, &m); 
    double left = inf, right = -inf; 
    int s = 0, t = 0; 
    for (int i = 1; i <= n; i++) { 
        scanf("%lf%lf", &px[i], &py[i]); 
        vertex[i].clear(); 
        if (px[i] < left) { 
            s = i; 
            left = px[i]; 
        } 
        if (px[i] > right) { 
            right = px[i]; 
            t = i; 
        } 
    } 
    int a, b; 
    for (int i = 0; i < m; i++) { 
        scanf("%d%d%lf", &a, &b, cap + i); 
        if (a == b) { 
            m--; 
            i--; 
            continue; 
        } 
        //①把所有的邊都拆成兩條有向邊,自環刪掉(有影響嗎?)。 
        double agab = atan2(py[b] - py[a], px[b] - px[a]); 
        double agba = atan2(py[a] - py[b], px[a] - px[b]); 
        //②將每條有向邊在另一個圖G‘中用一個點表示。 
        vertex[a].push_back(arch(i << 1, i << 1 | 1, agab)); 
        vertex[b].push_back(arch(i << 1 | 1, i << 1, agba)); 
    } 
    a = m << 1, b = a | 1; 
    //注意(S, T)的那條新加邊要特殊處理。 
    vertex[s].push_back(arch(a, b, acos(-1.0))); 
    vertex[t].push_back(arch(b, a, 0)); 

int nxt[maxm * 2], belong[maxm * 2], cnt; 
void find(int x, int root) { 
    if (nxt[x] != root) 
        find(nxt[x], root); 
    belong[x] = cnt; 

void build() { 
    //③將所有的與之相連的邊極角排序。 
    for (int i = 1; i <= n; i++) { 
        sort(vertex[i].begin(), vertex[i].end()); 
        int ms = vertex[i].size(); 
    //④遍歷每條入邊。將其後繼設為與之順時針相鄰的出邊。也就是在G’中連一條從這個入邊的點到其後繼的有向邊。 
        for (int j = 0; j < ms - 1; j++) 
            nxt[vertex[i][j].in] = vertex[i][j + 1].out; 
        nxt[vertex[i][ms - 1].in] = vertex[i][0].out; 
    } 
    int ms = m << 1 | 1; 
    memset(belong, -1, sizeof(belong)); 
    cnt = 0; 
    //⑤在G'中就是一些不相交的有向環。每個有向環就對應一個區域。找出了所有的區域,我們要的那張圖就簡單了。 
    for (int i = 0; i <= ms; i++) 
        if (belong[i] == -1 && ++cnt > 0) 
            find(i, i); 
    //構不成平面的邊,會形成一個特殊的區域,相當於進去死胡同再出來。但是答案不會受到影響,所以直接忽略。 

double work() { 
    int a, b; 
    sp.init(); 
    //⑥根據對偶圖構圖,求得s-t之間最短路即是對應的最小割 
    for (int i = 0; i < m; i++) { 
        a = belong[i << 1]; 
        b = belong[i << 1 | 1]; 
        sp.add(a, b, cap[i]); 
        sp.add(b, a, cap[i]); 
    } 
    a = belong[m << 1]; 
    b = belong[m << 1 | 1]; 
    return sp.solve(a, b); 

int main() { 
    int cas; 
    scanf("%d", &cas); 
    while (cas--) { 
        init(); 
        build(); 
        printf("%.0f\n", work()); 
    } 
    return 0; 


 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved