NOIP 2018旅行(暴力删边)

感觉这个题没什么好说的。

对于树的情况直接从1开始搜(保证字典序最小),从大到小排序它的子节点,再接着搜。

对于基环树,直接\(n^2\)暴力删边即可,用\(vector\)存边,排序预处理,枚举边,给边两侧的点打上标记,在搜到他们俩时直接continue就行,剩下的和树一样

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<vector>
using namespace std;
typedef long long ll;
const int maxm=10007;
int n,m;
int pre[maxm],last[5007],other[maxm],l,nxt[maxm];
bool vis[5007];
vector<int >e[5007];
int duanx,duany;
int ans[5007],cnt,q[5007];
void add(int x,int y)
{
 l++;
 pre[l]=last[x];
 last[x]=l;
 other[l]=y;
 nxt[l]=x;	
}
void dfs(int x)
{
 int pl[5007];//开在里面,避免下次直接覆盖 
 vis[x]=1;
 printf("%d ",x);
 int cnt=0;
 for(int p=last[x];p;p=pre[p])
 {
  int v=other[p];
  if(vis[v]) continue;
  pl[++cnt]=v;
 }
 if(cnt==0) return;
 sort(pl+1,pl+cnt+1);
 for(int i=1;i<=cnt;i++)
 if(!vis[pl[i]])dfs(pl[i]);
}

inline void lcx_change()
{
  for(register int i=1;i<=n;i++)
  ans[i]=q[i];
}
inline bool check()
{
 for(register int i=1;i<=n;i++)
 {
   if(q[i]==ans[i]) continue;
   else if(q[i]>ans[i]) return 0;
   else return 1;
 }
}
void wtz_AK(int x)
{
 vis[x]=1;
 q[++cnt]=x;	
 for(int i=0;i<e[x].size();i++)
 {
  int v=e[x][i];
  if(vis[v]||(duanx==x&&duany==v)||(duanx==v&&duany==x)) continue;
  wtz_AK(v);
 }
}
int main()
{
 scanf("%d%d",&n,&m);
 for(int i=1;i<=m;i++)
 {
  int x,y;
  scanf("%d%d",&x,&y);
  add(x,y);
  add(y,x);
  e[x].push_back(y);
  e[y].push_back(x);
 }
 if(m==n-1)dfs(1);
 else
 {
  for(int i=1;i<=n;i++)
  sort(e[i].begin(),e[i].end());
  for(register int i=1;i<=l;i+=2)
  {
    cnt=0;
    duanx=nxt[i];
    duany=other[i]; 
    wtz_AK(1);
	memset(vis,0,sizeof(vis));
    if(cnt!=n) continue;
    if(ans[1]==0)
    lcx_change();
    else if(check()) lcx_change();
	
  }
  for(int i=1;i<=n;i++)
  printf("%d ",ans[i]); 
 }
 return 0;	
}

感觉要多使stl啊

posted @ 2019-10-12 16:40  lihan123  阅读(116)  评论(0)    收藏  举报