1. 程式人生 > >@NOIP2018 - [email protected] 旅行

@NOIP2018 - [email protected] 旅行

目錄

【部落格搬運 * 4】


@題目描述@

小 Y 是一個愛好旅行的 OIer。她來到 X 國,打算將各個城市都玩一遍。

小Y瞭解到, X國的 n 個城市之間有 m 條雙向道路。每條雙向道路連線兩個城市。 不存在兩條連線同一對城市的道路,也不存在一條連線一個城市和它本身的道路。並且, 從任意一個城市出發,通過這些道路都可以到達任意一個其他城市。小 Y 只能通過這些道路從一個城市前往另一個城市。

小 Y 的旅行方案是這樣的:任意選定一個城市作為起點,然後從起點開始,每次可以選擇一條與當前城市相連的道路,走向一個沒有去過的城市,或者沿著第一次訪問該城市時經過的道路後退到上一個城市。當小 Y 回到起點時,她可以選擇結束這次旅行或 繼續旅行。需要注意的是,小 Y 要求在旅行方案中,每個城市都被訪問到。

為了讓自己的旅行更有意義,小 Y 決定在每到達一個新的城市(包括起點)時,將 它的編號記錄下來。她知道這樣會形成一個長度為 n 的序列。她希望這個序列的字典序 最小,你能幫幫她嗎?

輸入
輸入檔案共 m + 1 行。第一行包含兩個整數 n,m(m≤n),中間用一個空格分隔。
接下來 m 行,每行包含兩個整數 u,v(1≤u,v≤n) ,表示編號為 u 和 v 的城市之間有一條道路,兩個整數之間用一個空格分隔。

輸出
輸出檔案包含一行,n 個整數,表示字典序最小的序列。相鄰兩個整數之間用一個空格分隔。

輸入樣例#1
6 5
1 3
2 3
2 5
3 4
4 6
輸出樣例#1
1 3 2 5 4 6

輸入樣例#2
6 6
1 3
2 3
2 5
3 4
4 5
4 6
輸出樣例#2
1 3 2 4 5 6

資料規模與約定
對於 100% 的資料和所有樣例, 1≤n≤5000 且 m = n − 1 或 m = n。

@題解@

據說這道題有很神奇的做法……可以 O(n) …… orz
但是介於我實在太弱了,所以只能在此向大家介紹 O(n^2) 的一個簡單演算法。

不難發現題目給出的其實要麼是樹要麼是基環樹。
樹的話只需要貪心地走它編號最小的兒子就可以實現字典序最小。
如果是基環樹的話,我們需要轉化成一棵樹。
基環樹轉樹最常見的肯定是刪去某一條環上的邊。

所以,如果是基環樹,我們就列舉一條環上的邊,將它打上標記。再 dfs 一遍(要求不經過這條被標記的邊),求得一個答案。最後再所有的答案取一個最小值。

以 1 為起點顯然比較優秀。

@程式碼@

因為是第一題所以比較輕鬆。
可能是因為害怕選手 D2 甚至無法 AC 一道題,然後怒而退役吧。

#include<queue>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN = 5000;
struct node{
    int num, to;
    node(int _n, int _t):num(_n), to(_t){}
};
bool operator < (node a, node b) {
    return a.to < b.to;
}
vector<node>G[MAXN + 5];
void addedge(int u, int v, int i) {
    G[u].push_back(node(i, v));
    G[v].push_back(node(i, u));
}
bool vis[MAXN + 5];
int res[MAXN + 5], ans[MAXN + 5], cnt;
void init(int n) {
    for(int i=1;i<=n;i++)
        vis[i] = false;
    cnt = 0;
}
int tag[MAXN + 5], deg[MAXN + 5];
queue<int>que;
void find_circle(int n) {
    for(int i=1;i<=n;i++)
        if( deg[i] == 1 ) que.push(i);
    while( !que.empty() ) {
        int f = que.front(); que.pop();
        vis[f] = true;
        for(int i=0;i<G[f].size();i++) {
            if( vis[G[f][i].to] ) continue;
            tag[G[f][i].num] = 1;
            deg[G[f][i].to]--;
            if( deg[G[f][i].to] == 1 )
                que.push(G[f][i].to);
        }
    }
}
void dfs(int x) {
    res[++cnt] = x; vis[x] = true;
    for(int i=0;i<G[x].size();i++)
        if( !vis[G[x][i].to] && tag[G[x][i].num] != -1 )
            dfs(G[x][i].to);
}
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i=1;i<=m;i++) {
        int u, v;
        scanf("%d%d", &u, &v);
        addedge(u, v, i); deg[u]++, deg[v]++;
    }
    for(int i=1;i<=n;i++)
        sort(G[i].begin(), G[i].end());
    if( n == m ) {
        init(n); find_circle(n); ans[1] = -1;
        for(int i=1;i<=m;i++) {
            if( !tag[i] ) {
                tag[i] = -1; init(n); dfs(1); tag[i] = 0;
                bool flag = false;
                if( ans[1] == -1 ) flag = true;
                else {
                    for(int i=1;i<=cnt;i++) {
                        if( ans[i] > res[i] ) {
                            flag = true;
                            break;
                        }
                        else if( ans[i] < res[i] )
                            break;
                    }
                }
                if( flag ) {
                    for(int i=1;i<=cnt;i++)
                        ans[i] = res[i];
                }
            }
        }
        for(int i=1;i<n;i++)
            printf("%d ", ans[i]);
        printf("%d\n", ans[n]);
    }
    else {
        init(n); dfs(1);
        for(int i=1;i<cnt;i++)
            printf("%d ", res[i]);
        printf("%d\n", res[cnt]);
    }
}