noip 2018 d2t1 旅行
noip 2018 d2t1 旅行
(題目來自洛谷)
給定n個城市,m條雙向道路的圖, 不存在兩條連接同一對城市的道路,也不存在一條連接一個城市和它本身的道路。並且, 從任意一個城市出發,通過這些道路都可以到達任意一個其他城市。小 Y 只能通過這些道路從一個城市前往另一個城市。
小 Y 的旅行方案是這樣的:任意選定一個城市作為起點,然後從起點開始,每次可 以選擇一條與當前城市相連的道路,走向一個沒有去過的城市,或者沿著第一次訪問該 城市時經過的道路後退到上一個城市。當小 Y 回到起點時,她可以選擇結束這次旅行或 繼續旅行。需要註意的是,小 Y 要求在旅行方案中,每個城市都被訪問到。(註意:同一條道路不能走兩遍,也就是回頭路只能走一次)
為了讓自己的旅行更有意義,小 Y 決定在每到達一個新的城市(包括起點)時,將 它的編號記錄下來。她知道這樣會形成一個長度為 nn 的序列。她希望這個序列的字典序 最小,你能幫幫她嗎? 對於兩個長度均為 n 的序列 A和 B,當且僅當存在一個正整數 x,滿足以下條件時, 我們說序列 A 的字典序小於 B。
對於任意正整數 1≤i<x,序列A的第 i 個元素 Ai 和序列 B 的第 i 個元素 Bi 相同。
序列 A 的第 x 個元素的值小於序列 B 的第 x 個元素的值。
輸入格式:
輸入文件共 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
說明
【數據規模與約定】
題解:
首先考慮m==n-1的情況,這種情況下,我們只需要先貪心的經過每個點所連向的點中最小的點,即可得到答案,直接用vector存圖,對點進行排序,在樹上dfs一遍即可解決,即可得到60分
考慮滿分做法,因為n==m則圖中有且只有一個環,可以想到這個環上肯定有一條邊是不被需要的,因為我們只需要走一遍即可。
我們先找到環,枚舉這個環上的每一條邊刪去,搜索更新答案的最小值,最後輸出最小值即可,時間復雜度n^2
不找環直接暴力刪邊也可過
找環可以拓撲排序,可以並查集,這裏采用一種其他的方法
#include<bits/stdc++.h>
#define maxn 5050
using namespace std;
struct node {
int v, w;
node(int vv) {
v = vv;
}
};
vector<node> G[maxn], g[maxn];
void adde(int a, int b) {
G[a].push_back(node(b));
G[b].push_back(node(a));
}
inline int getnum() {
int ans = 0; char c; int flag = 1;
while (!isdigit(c = getchar()) && c != '-');
if (c == '-') flag = -1; else ans = c - '0';
while (isdigit(c = getchar())) ans = ans * 10 + c - '0';
return ans * flag;
}
int n, m;
int vis[maxn];
int cnt = 0;
int ans[maxn], tmp[maxn];
int del[maxn][maxn];
int cmp(node a, node b) {
return a.v < b.v;
}
void dfs(int u, int f) {//假如n == m - 1,則直接貪心選取較小的邊,對連向的點進行排序直接遍歷圖即可
ans[++cnt] = u;
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i].v;
if (v != f)
dfs(v, u);
}
}
int num;
int find(int u, int f) {//找環
if (num) return 0;//假如已經找到了環的起點,則退出搜索,因為此時環已經構建好了
vis[u] = 1;//已經遍歷過這個點
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i].v;
if (v == f) continue;
if (vis[v]) {//假如找到了遍歷過的點,說明形成了環
num = v;//環的起點為num
g[u].push_back(node(v));//將構成環的這條邊加入
return 1;//返回1,代表可以找到環
}
if (find(v, u)) {//假如從v開始能找到環的話
if (v == num) return 0;//如果連向的點就是起點,則返回,因為這條邊不在環內
g[u].push_back(node(v));//將在環內的邊加入圖g
return 1;//返回可以找到環,這樣就可以將一整個環內的邊全部加入g
}
}
}
void dfs1(int u) {
if (vis[u]) return;
vis[u] = 1;
tmp[++cnt] = u;
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i].v;
if (del[u][v] == 0) dfs1(v);
}
}
int check() {
for (int i = 1; i <= n; i++) {
if (ans[i] == tmp[i]) continue;
if (ans[i] > tmp[i]) return 1;
else return 0;
}
}
int main() {
n = getnum(), m = getnum();
for (int i = 1; i <= m; i++) {
int a = getnum(), b = getnum();
adde(a, b);
}
for (int i = 1; i <= n; i++) {
sort(G[i].begin(), G[i].end(), cmp);//排序
}
for (int i = 1; i <= n; i++) {
ans[i] = n;//將ans賦值為最大值
}
if (n == m) {
find(1, -1);
for (int i = 1; i <= n; i++) {
for (int j = 0; j < g[i].size(); j++) {
memset(vis, 0, sizeof(vis));
int v = g[i][j].v;
del[i][v] = 1; del[v][i] = 1;//刪邊
cnt = 0;
dfs1(1);
del[i][v] = 0; del[v][i] = 0;//恢復
if (check()) {
for (int i = 1; i <= n; i++) {
ans[i] = tmp[i];
}
}
}
}
for (int i = 1; i <= n; i++) {
cout << ans[i] << " ";
}
return 0;
}
dfs(1, -1);
for (int i = 1; i <= cnt; i++) {
cout << ans[i] << " ";
}
return 0;
}
noip 2018 d2t1 旅行