日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

P5049 [NOIP2018 提高组] 旅行

發(fā)布時(shí)間:2023/12/3 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 P5049 [NOIP2018 提高组] 旅行 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

P5049 [NOIP2018 提高組] 旅行

題意:

一棵樹(可能是基環(huán)樹),從1出發(fā),每到達(dá)一個(gè)新的點(diǎn)就記錄下編號(hào)。求一種走法使得記錄下來的編號(hào)字典序最小。
1≤n≤500000
m=n?1 或 m=n

題解:

如果不是基環(huán)樹,那直接每次走字典序小的點(diǎn)即可
對(duì)于基環(huán)樹:
第一個(gè)方法:
暴力刪邊將基環(huán)樹變?yōu)橐活w普通的樹,然后計(jì)算答案,復(fù)雜度是O(n * n)
這個(gè)方法好像只能過P5022 [NOIP2018 提高組] 旅行 這個(gè)沒加強(qiáng)數(shù)據(jù)的題,P5049過不了
第二個(gè)方法:
參考題解
對(duì)于基環(huán)樹,我們?cè)诃h(huán)上跑到一半,另一半通過回溯到你剛到這個(gè)環(huán)的起點(diǎn),接著DFS就可以了
例如下圖,我們從1開始,走3走2然后回溯3,走4,走5回溯4,最后走6
如果在環(huán)上回溯之后(圖中是2回溯到3),剩下的就不需要特殊處理

我們把在環(huán)上的點(diǎn)分成三種情況:
一、其出邊為環(huán)上的那個(gè)點(diǎn)編號(hào)是其所有未被訪問的出邊中最小的,如下圖。
相比于6和7,4更優(yōu),所以第一種情況不需要回溯,繼續(xù)環(huán)上走就行

二:其出邊為環(huán)上的那個(gè)點(diǎn)編號(hào)是其所有未被訪問的出邊中不是最大也不是最小的,如下圖
先走4,然后走6,不需要回溯,繼續(xù)環(huán)上走

三:其出邊為環(huán)上的那個(gè)點(diǎn)編號(hào)是其所有未被訪問的出邊中最大的,如下圖。
先走4,再走6,再回溯2

總結(jié)一下:
當(dāng)我們?cè)诃h(huán)上走時(shí),只要當(dāng)其出邊中,在環(huán)上的那個(gè)點(diǎn)的編號(hào)最大時(shí),且比回溯后第一個(gè)走的點(diǎn)還大,這時(shí)才回溯,其他都不用回溯正常跑DFS即可
我們用flag標(biāo)記是否需要回溯,用tmp記錄當(dāng)前節(jié)點(diǎn)中第一個(gè)比環(huán)上的出邊的節(jié)點(diǎn)還要大的節(jié)點(diǎn),以便后面判斷是否回溯

代碼:

方法一:

#include<cstdio> #include<queue> #include<cstring> #include<algorithm> #define N 5010 using namespace std; struct node{int to,next; }a[2*N]; struct line{int x,y; }l[2*N]; int tot,n,m,t,ls[N],in[N],state[N],w[N],ans[N],x,y,q[N]; bool k[N][N],v[N]; void addl(int x,int y)//加邊 {a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;in[y]++; } bool topsort(){int l=0,r=0;for (int i=1;i<=n;i++) if(in[i]==1) q[++r]=i;while(l<r) {int now=q[++l];for (int i=ls[now];i;i=a[i].next){int y=a[i].to;if(in[y]>1){in[y]--;if(in[y]==1) q[++r]=y;}}}if(r==n) return true;return false; }//拓?fù)淝蟓h(huán) bool cmp(line x,line y) {return x.y>y.y;} void dfs(int x)//走一遍 {state[++t]=x;v[x]=true;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(k[x][y]||v[y]) continue;dfs(y);} } void check()//判斷是否為更小字典序 {int i;bool flag=false;for(i=1;i<=n;i++)if(state[i]<ans[i]){flag=true;break;}else if(state[i]>ans[i]) return;if(!flag) return;for(;i<=n;i++)ans[i]=state[i]; } void get_ans(int xs)//暴力刪邊 {int x=xs,b=0,i,last=0;do{w[++b]=x;in[x]=1;for(i=ls[x];i;i=a[i].next){int y=a[i].to;if(in[y]>1){x=y;break;}}}while(i);//記錄環(huán)的每個(gè)點(diǎn)w[++b]=xs;for(int i=1;i<b;i++)//枚舉刪除的邊{k[w[i]][w[i+1]]=k[w[i+1]][w[i]]=true;memset(v,0,sizeof(v));t=0;dfs(1);check();k[w[i]][w[i+1]]=k[w[i+1]][w[i]]=false;} } int main() {memset(ans,127/3,sizeof(ans));scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){scanf("%d%d",&x,&y);l[i]=(line){x,y};l[i+m]=(line){y,x};}sort(l+1,l+1+2*m,cmp);//排序tot=1;for(int i=1;i<=2*m;i++)//加邊{addl(l[i].x,l[i].y);}if(m==n-1)//普通的樹{dfs(1);for(int i=1;i<=n;i++)printf("%d ",state[i]);return 0;}topsort();for(int i=1;i<=n;i++)if(in[i]>1){get_ans(i);break;}for(int i=1;i<=n;i++)printf("%d ",ans[i]); }

方法二:

#include <iostream> #include <cstdio> #include <algorithm> #include <stack> #include <queue> #include <cstring> #include <vector> using namespace std; const int N = 500010; int n, m, vis[N], ans[N], cnt, f[N], rings[N], flag, tmp, temp, head[N], ver[N << 1], nex[N << 1], tot; struct Node {int x, y; }node[N << 1]; void add (int x, int y) {ver[++ tot] = y;nex[tot] = head[x];head[x] = tot; } bool cmp (Node a, Node b) {return a.y > b.y; } inline int read () {int res = 0;char ch = getchar();while (ch < '0' || ch > '9') ch = getchar();while (ch >= '0' && ch <= '9') {res = (res << 3) + (res << 1) + (ch - 48);ch = getchar();}return res; } void dfs (int x) {vis[x] = 1;ans[++ cnt] = x;for (int i = head[x]; i; i = nex[i]) {int y = ver[i];if (!vis[y])dfs(y);} } void dfsRing (int x, int fa) {if (flag) return;if (f[x] == 0) {f[x] = fa;}else if (f[x] != fa) {while (fa != x) {rings[fa] = 1;fa = f[fa];}rings[x] = 1;flag = 1;return;}for (int i = head[x]; i; i = nex[i]) {int y = ver[i];if (y == fa) continue;dfsRing(y, x);} } void sDfs (int x) {vis[x] = 1;ans[++ cnt] = x;if (rings[x]) { //判斷x是否在環(huán)上 int flag = 0;for (int i = head[x]; i; i = nex[i]) {if (temp) break; //temp標(biāo)記環(huán)上的回溯是否執(zhí)行過了,因?yàn)橐坏﹫?zhí)行過環(huán)上的回溯,那么后面就不需要在環(huán)上回溯,只需正常跑DFS即可 int y = ver[i];if (vis[y]) continue;if (rings[y]) {i = nex[i];while (vis[ver[i]]) //已經(jīng)被訪問過的節(jié)點(diǎn)跳過 i = nex[i];if (i) //i不為0即環(huán)上的出邊不是最大的出邊 tmp = ver[i]; //tmp記錄第一個(gè)比環(huán)的出邊大的那個(gè)點(diǎn) else if (y > tmp) { //環(huán)上的出邊是最大的出邊且比我們回溯后第一次要走的節(jié)點(diǎn)還大 flag = 1;temp = 1;}break;}}for (int i = head[x]; i; i = nex[i]) {int y = ver[i];if (vis[y]) continue;if (rings[y] && flag) continue; //flag = 1,因此回溯,不再走環(huán)上的出邊 sDfs(y);}} else {for (int i = head[x]; i; i = nex[i]) {int y = ver[i];if (vis[y]) continue;sDfs(y);}} } int main () {n = read();m = read();for (int i = 1; i <= m; i ++) {int u = read(), v = read();node[i].x = u;node[i].y = v;node[i + m].x = v;node[i + m].y = u;}sort(node + 1, node + 2 * m + 1, cmp);for (int i = 1; i <= 2 * m; i ++)add(node[i].x, node[i].y);if (m == n - 1) {dfs(1);for (int i = 1; i <= n; i ++)printf("%d ", ans[i]);}else {dfsRing(1, 1); //一開始先找出所有在環(huán)上的點(diǎn) flag = 0;tmp = 0x3f3f3f3f;sDfs(1);for (int i = 1; i <= n; i ++)printf("%d ", ans[i]);}return 0; }

總結(jié)

以上是生活随笔為你收集整理的P5049 [NOIP2018 提高组] 旅行的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。