1. 程式人生 > >5964. 【NOIP2018提高組D2T1】旅行

5964. 【NOIP2018提高組D2T1】旅行

Description

小Y是一個愛好旅行的OIer。她來到X國,打算將各個城市都玩一遍。
小Y瞭解到,X國的n個城市之間有m條雙向道路。每條雙向道路連線兩個城市。不存在兩條連線同一對城市的道路,也不存在一條連線一個城市和它本身的道路。並且,從任意一個城市出發,通過這些道路都可以到達任意一個其他城市。小Y只能通過這些道路從一個城市前往另一個城市。
小Y的旅行方案是這樣的:任意選定一個城市作為起點,然後從起點開始,每次可以選擇一條與當前城市相連的道路,走向一個沒有去過的城市,或者沿著第一次訪問該城市時經過的道路後退到上一個城市。當小Y回到起點時,她可以選擇結束這次旅行或繼續旅行。需要注意的是,小Y要求在旅行方案中,每個城市都被訪問到。
為了讓自己的旅行更有意義,小Y決定在每到達一個新的城市(包括起點)時,將它的編號記錄下來。她知道這樣會形成一個長度為n的序列。她希望這個序列的字典序最小,你能幫幫她嗎? 
對於兩個長度均為n的序列A和B,當且僅當存在一個正整數x,滿足以下條件時,我們說序列A的字典序小於B。
①對於任意正整數1<=i<x,序列A的第i 個元素Ai 和序列B的第i 個元素Bi相同。
②序列A的第x個元素的值小於序列B的第x個元素的值。

Input

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

Output

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

Sample Input

輸入1:
6 5
1 3
2 3
2 5
3 4
4 6

輸入2:
6 6
1 3
2 3
2 5
3 4
4 5
4 6

Sample Output

輸出1:
1 3 2 5 4 6

輸出2:
1 3 2 4 5 6

Data Constraint

Source / Author: travel

Solution

1.注意到每經過一條新的邊,必定訪問了一個新的點。除了起點外,恰好訪問了n-1個新的點,也就是整個過程只會經過n-1條邊。這意味著經過的邊形成了一棵生成樹。若原圖就是一棵樹,我們考慮如何計算最優字典序。首先,我們一定選擇 號點為起點。可以發現題目中的限制等 價於對原圖的一次dfs,形成的序列就是dfs序。因此我們只需要確定訪問孩子的順序使得dfs序字典序最小,這顯 然只需要按照編號從小到大訪問孩子即可。算上排序,對樹求答案的複雜度就是O(n long n)。

2.如果m=n,根據之前的結論一定有某一條邊沒有經過,可以列舉這是哪一條邊,如果剩下的圖是一棵樹,就按照樹的方法做一遍,然後更新答案。複雜度O(n^2 log n)。 不過我們並不需要每次都對節點排序,只需要開始時對每個節點的相鄰節點排序即可,複雜度為O(n^2)。如果這樣還是超時的話,其實可以不用找環,再刪掉環上的邊,我們可以每次在遞迴用棧統計答案的時候判斷一下如果棧的當前這一位的前面所有的位置都不比答案大,且當前這一位比答案的這一位要大的話就可以直接推出。

Code1

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=(a);i<=(b);++i)
#define fd(i,a,b) for(int i=(a);i>=(b);--i)
#define bfo(i,v,u) for(int i=BB[v],u=B[i][1];i;u=B[i=B[i][0]][1])
#define mset(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
int read()
{
	char ch;int n=0,p=1;
	for(ch=getchar();ch<'0' || '9'<ch;ch=getchar()) if(ch=='-') p=-1;
	for(;'0'<=ch && ch<='9';ch=getchar()) n=n*10+ch-'0';
	return n*p;
}
const int N=5005,M=N<<1;
struct edge
{
	int x,y,id;
	friend bool operator < (edge a,edge b) {return a.x!=b.x?a.x<b.x:a.y<b.y;}
}b[M];
int n,m,B0,BB[N],B[M][3];
void link(int u,int v,int id){B[++B0][1]=v,B[B0][2]=id,B[B0][0]=BB[u],BB[u]=B0;}
int cut;
bool bz[N];
void getans(int v)
{
	printf("%d ",v);
	bz[v]=1;
	bfo(i,v,u)
		if(!bz[u] && B[i][2]!=cut) getans(u);
}
bool vis[N],incir[M];
int stk[N];
bool findcir(int v,int fr=0)
{
	if(vis[v])
	{
		int i=stk[0]-1;
		while(i && B[stk[i]][1]!=v) i--;
		fo(j,i+1,stk[0]) incir[B[stk[j]][2]]=1;
		return 1;
	}
	vis[v]=1;
	bfo(i,v,u) if(u!=fr)
	{
		stk[++stk[0]]=i;
		if(findcir(u,v)) return 1;
		stk[stk[0]--]=0;
	}
	return 0;
}
void dfs(int v,int fr=0,int p=-1)
{
	if(vis[v]) return;
	vis[v]=1;
	int i=BB[v];
	if(B[i][1]==fr) i=B[i][0];
	for(int nxt=B[i][0],u=B[i][1];i;u=B[i=nxt][1],nxt=B[i][0])
	{
		if(B[nxt][1]==fr) nxt=B[nxt][0];
		if(!nxt)
		{
			if(p!=-1 && p<u && incir[B[i][2]]) cut=B[i][2];
			else dfs(u,v,p);
		}
		else
		dfs(u,v,B[nxt][1]);
		if(cut) return;
	}
}
int main()
{
	freopen("travel.in","r",stdin);
	freopen("travel.out","w",stdout);
	n=read(),m=read();
	fo(i,1,m)
	{
		int x=read(),y=read();
		b[i+i]=(edge){x,y,i},b[i+i+1]=(edge){y,x,i};
	}
	sort(b+2,b+2*m+2);
	B0=1;
	for(int l=2,r=2;r<=2*m+1;l=r)
	{
		for(;b[r].x==b[l].x && r<=2*m+1;++r);
		fd(i,r-1,l) link(b[i].x,b[i].y,b[i].id);
	}
	if(m==n-1) return getans(1),0;
	findcir(1);
	mset(vis,0);
	dfs(1);
	getans(1);
	return 0;
}

Code2 

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 5010
using namespace std;
int n,m,x,y,t[N*2],nx[N*2],l[N],bz[N],ans[N],s[N],w[N][N],sh[N],b[N][N],h=0,bo;
void add(int x,int y){
	t[++t[0]]=y;
	nx[t[0]]=l[x];
	l[x]=t[0];
}
void dg(int x){
	if(!s[0]) return;
	for(int i=1;i<=w[x][0];i++){
		if(!bz[w[x][i]]&&!b[x][w[x][i]]){
			bz[w[x][i]]=1;
			s[++s[0]]=w[x][i];
			if(s[s[0]]<ans[s[0]]) bo=1;
			if(s[s[0]]>ans[s[0]]&&!bo){
				s[0]=0;return;
			}
			dg(w[x][i]);
		}
	}
}
int bj(){
	for(int i=1;i<=n;i++){
		if(s[i]<ans[i]) return 1;
		if(ans[i]<s[i]) return 0;
	}
	return 0;
}
int main(){
	freopen("travel.in","r",stdin);
	freopen("travel.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		scanf("%d%d",&x,&y);
		w[x][++w[x][0]]=y;
		w[y][++w[y][0]]=x;
	}
	for(int i=1;i<=n;i++){
		sort(w[i]+1,w[i]+w[i][0]+1);
		ans[i]=n+2;
	}
	if(m==n-1){
		bz[1]=s[++s[0]]=1;
		dg(1);
		for(int i=1;i<=n;i++) printf("%d ",s[i]);
		return 0;
	}
	for(int i=1;i<=m;i++){
		for(int j=1;j<=w[i][0];j++){
			b[i][w[i][j]]=b[w[i][j]][i]=1;
			memset(bz,0,sizeof(bz));
			bz[1]=s[s[0]=1]=bz[0]=1;
			bo=0;
			dg(1);
			bz[0]=1;
			for(int k=1;k<=n;k++){
				if(!bz[k]){
					bz[0]=0;
					break;
				}
			}
			if(bz[0]){
				if(bj()){for(int i=1;i<=n;i++) ans[i]=s[i];}
			}
			b[i][w[i][j]]=b[w[i][j]][i]=0;
		}
	}
	for(int i=1;i<=n;i++) printf("%d ",ans[i]);
	return 0;
}