1. 程式人生 > >【HDU - 6311】Cover

【HDU - 6311】Cover

@[email protected]


@題目描述 - [email protected]

The Wall has down and the King in the north has to send his soldiers to sentinel.
The North can be regard as a undirected graph (not necessary to be connected), one soldier can cover one path. Today there’s no so many people still breathing in the north, so the King wants to minimize the number of soldiers he sent to cover each edge exactly once. As a master of his, you should tell him how to arrange soldiers.

Input
There might be multiple test cases, no more than 20. You need to read till the end of input.
In the first line, two integers n and m, representing the number of nodes and edges in the graph.
In the following m lines, each contain two integers, representing two ends of an edge.
There are no parallel edges or self loops.
1≤n,m≤100000

Output
For each test case, the first line contains number of needed routes, p.
For the following p lines, an integer x in the beginning, followed by x integers, representing the list of used edges. Every integer should be a positive or negative integer. Its absolute value represents the number of chosen edge (1~n). If it’s positive, it shows that this edge should be passed as the direction as the input, otherwise this edge should be passed in the direction different from the input. Edges should be in correct order.

Sample Input
3 3
1 2
1 3
2 3
Sample Output
1
3 1 3 -2

@大致題意@

給定一個無向無自環無重邊的圖,求最少使用多少條路徑才能將所有邊覆蓋。路徑可以包含重複的點但不能包含重複的邊。輸出方案,詳細輸出格式見英文題目。

@分析@

假設只用一條路徑就可以覆蓋所有邊,那麼這個圖一定是一個歐拉回路或尤拉路徑,即不包含奇數度的點或只包含兩個奇數度的點的圖。尤拉路徑有著這樣一個推廣的結論:假設一個聯通塊內包含n個奇數度的點(n≠0),那麼至少需要n/2條路徑才可以將所有邊覆蓋。
所以我們這樣統計答案:對於一個聯通塊,假設它裡面不含奇點,並且它的大小≠1(注意必須要這個特判),則答案+1;如果它包含奇點,統計它包含有多少個奇點,答案+奇點數/2。

對於簡單的歐拉回路與尤拉路徑,我們可以很簡單的輸出方案。但是對於推廣出來的結論,我們應該如何輸出方案?這其實是一個很經典的尤拉路徑擴展出來的問題,很多有關尤拉路徑的難題大都是由這個問題變形而來。

我們這樣構造:對於某一個聯通塊,把塊內的奇點兩兩一組,將每一組內的兩個點用一條虛邊連線。這樣就會連線n/2條虛邊,並且連線虛邊後的新圖並不存在奇點。在新圖上跑歐拉回路,就可以得到一個邊序列 A。注意到這個邊序列其實是圓環形的,即 A[1] 與 A[N] 是可以連在一起的【因為它是歐拉回路,從任何一條邊出發都是等價的】。因為有n/2條虛邊,就會產生n/2個不含虛邊的連續序列,且每個序列之間都間隔一個虛邊。這時我們發現,這n/2個連續邊序列恰好就是我們所需要的方案!同時我們發現,這樣子同時也就證明了推廣定理的正確性。
實現上,為了避免圓環形序列,我們總讓邊序列的第一個元素為虛邊。

於是本題到這裡就解決得差不多了。

@程式碼@

【當初我做Hack It做到自閉時,隊友就是做這道題做到自閉……】
【唉……見的題目太少了QAQ……果然我們只是辣雞啊QAQ……】
程式碼的實現可能有些細節上面沒有提到,不過大體思路還是按照上面來的,可以參考一下程式碼細節以更好地理解。
如果有什麼疑問就可以評論下面,我會盡力解疑的~

#include<cstdio>
#include<vector>
using namespace std;
const int MAXN = 100000;
struct edge{
    int to, num; bool vis;
    edge *nxt, *rev;
}edges[4*MAXN + 5], *ecnt, *adj[MAXN + 5];
int n, m, deg[MAXN + 5], a[MAXN + 5];
int hve[MAXN + 5], siz[MAXN + 5], fa[MAXN + 5];
vector<int>arr, ans;
void addedge(int u, int v, int w) {
    edge *p = (++ecnt);
    p->num = w, p->to = v, p->vis = false;
    p->nxt = adj[u], adj[u] = p;
    edge *q = (++ecnt);
    q->num = -w, q->to = u, q->vis = false;
    q->nxt = adj[v], adj[v] = q;
    q->rev = p, p->rev = q;
}
void init() {
    for(int i=1;i<=n;i++) {
        a[i] = -1;
        deg[i] = 0;
        siz[i] = 1;
        fa[i] = i;
        adj[i] = NULL;
        hve[i] = 0;
    }
    ecnt = &edges[0];
}
int Find(int x) {
    if( fa[x] == x ) return x;
    else return fa[x] = Find(fa[x]);
}
void Union(int x, int y) {
    int fx = Find(x), fy = Find(y);
    if( fx != fy )
        fa[fx] = fy, siz[fy] += siz[fx];
}
void dfs(int x) {
    for(edge *p=adj[x];p!=NULL;p=p->nxt) {
        if( !p->vis ) {
            p->vis = p->rev->vis = true;
            dfs(p->to);
            arr.push_back(p->num);
        }
    }
}
void solve() {
    init();
    for(int i=1;i<=m;i++) {
        int u, v;
        scanf("%d%d", &u, &v);
        addedge(u, v, i);
        Union(u, v);
        deg[u]++, deg[v]++;
    }
    for(int i=1;i<=n;i++) {
        if( deg[i] & 1 ) {
            hve[Find(i)]++;
            if( a[Find(i)] == -1 )
                a[Find(i)] = i;
            else {
                addedge(a[Find(i)], i, 0);//令虛邊編號為0,實邊編號為非0數 
                a[Find(i)] = -1;
            }
        }
    }
    int tot = 0;
    for(int i=1;i<=n;i++) {
        if( fa[i] != i ) continue;
        if( !hve[i] ) {
            if( siz[i] != 1 )
                tot++;
        }
        else tot += hve[i]/2;
    }
    printf("%d\n", tot);
    for(int i=1;i<=n;i++) {
        if( hve[Find(i)] ) {
            if( deg[i] & 1 ) {
                dfs(i);
//因為指標版鄰接表的特性,後加入的邊就會在最前面,所以就一定保證了第一條訪問到的邊一定是虛邊 
                if( arr.empty() ) continue;
                int p = arr.size()-2;
                while( p >= 0 ) {
                    if( arr[p] == 0 ) {
                        printf("%d", int(ans.size()));
                        for(int i=0;i<ans.size();i++)
                            printf(" %d", ans[i]);
                        puts("");
                        ans.clear();
                        p--;
                    }
                    ans.push_back(arr[p]);
                    p--;
                }
                printf("%d", int(ans.size()));
                for(int i=0;i<ans.size();i++)
                    printf(" %d", ans[i]);
                puts("");
                ans.clear(); arr.clear();
            }
        }
        else {
            dfs(i);
            if( arr.empty() ) continue;
            printf("%d", int(arr.size()));
            for(int i=arr.size()-1;i>=0;i--)
                printf(" %d", arr[i]);
            puts("");
            arr.clear();
        }
    }
}
int main() {
    while( scanf("%d%d", &n, &m) == 2 )
        solve();
}

@[email protected]

就是這樣,新的一天裡,也請多多關照哦(ノω<。)ノ))☆.。~