UVa 10054 项链

题意:有一串珠子,每个珠子的前半部分和后半部分都有一种颜色。两个珠子颜色相同的部分,可以连到一起。给定一些珠子看是否能用所有这些珠子串成一个项链。

思路:将每个颜色看做一个顶点,每个珠子对应一种颜色到另一颜色的无向边。每个珠子只能用一次,相当于每条边只能用一次。欧拉回路。不过不同的是,这道题需要输出欧拉回路的路径,而且下一路径的起点必须是上一路径的重点。我觉得难点是在这个路径的输出上。    开始的时候我是在dfs()里用 printf("%d%d",n,i); dfs(i);(方式1) 这样是不能满足要求的。比如对边集合 1 1、1 2、2 1、2 3、3 2来说,上述代码就会打印出1 1、1 2、2 1、2 3、3 2。而是应该用 dfs(i); printf("%d%d",n,i); (方式2)输出路径,这样的话,每次递归 dfs(i); 时,函数dfs(n); 的栈帧相当于入栈了,即相当于边 n-i 入栈。一直到遇到一个没有邻边没被访问的顶点时,才开始弹栈输出边。比如上述示例,开始压栈1 1、1 2、2 1,再dfs(1)时发现1顶点没有未访问的边了,于是弹栈输出边2-1,继续找2的邻接边,发现是3,压栈2 3、3 2,dfs(2)发现没有未访问的边,弹栈输出。此外,由于要求路径的起点和上一路径的终点要相同,所有输出的时候应改为 printf("%d %d",i,n); 这个也好理解,比如之前实例是压栈1 1、1 2、2 1,然后发现需要弹栈输出了,因为顺着到达1这个没有未访问边的顶点时,路径上是首尾相接的,所以你反着输出也就正好是首尾相接的,比如如果只有这三条边的话,你反着输出就是1-2、2-1、1-1。

更直观的是对于欧拉道路的输出,用 dfs(i); printf("%d %d",i,n); 相当于你从起点首尾相接地走出一条到终点的道路,然后到终点时,发现没有未访问的边则开始回退,用print(i,n)将边的顶点反过来,相当于输出一条从终点首尾相接回退到起点的路径。

对于路径输出的理解还有一个例子,比如有边集合1 3、3 3、3 5,输出欧拉道路。用方式1输出的话,意思是每遇到一条路径时就输出,这样不能保证连续,比如这里会输出1 3、3 3、3 5,但如果重点改为2呢,即5改为2,则输出1 3、3 2、3 3,相当于从起点到终点后再回退到上一顶点打印另一条路径。而用方式2输出,它是在遇到一个没有邻边的顶点时才输出,输出的是3 5、3 3、1 3 和 3 2、3 3、1 3。需要首尾相接,就将 n,i 调换为 i,n 。

针对这道题,我是判断了连通性,而且判断连通性和找路径是在一起进行的,所以用一个数组保存了路径,在判断连通后才输出。开始觉得是首尾相接的,就只保存一个点,然后输出时用print(i,i+1); 结果出现错误,很难发现~

Code:

#include<stdio.h>
#include<string.h>

bool solve();
void dfs(int n);

int graph[51][51];
int du[51];
int path[1010*2];
int len;

int main()
{
  //freopen("10054.in","r",stdin);
  //freopen("10054.out","w",stdout);
  int t;
  scanf("%d",&t);
  int num=0;
  while(t-->0)
  {
    num++;
    memset(graph,0,sizeof(graph));
    memset(du,0,sizeof(du));
    len=0;
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;++i)
    {
      int a,b;
      scanf("%d%d",&a,&b);
      du[a]++;
      du[b]++;
      graph[a][b]++;//增加一条无向边
      //graph[b][a]=graph[a][b]; 
      graph[b][a]++;
    }
    if(num!=1) printf("\n");
    printf("Case #%d\n",num);
    int len=0;
    if(solve()==false) printf("some beads may be lost\n");
  }
  return 0;   
}

bool solve()
{
  //判定顶点度数 
  for(int i=1;i<=50;++i)
  {
    if(du[i]%2==0) continue;
    else return false;
  }//printf("ok\n");
  //判定连通性 
  int cnt=0;//连通块个数 
  for(int i=1;i<=50;i++)
  {
    if(du[i])//存在该颜色 
    {
      cnt++; dfs(i);
    }
  }
  if(cnt>1) return false;
  //print
  //printf("len:%d\n",len);
  for(int i=0;i<len-1;i=i+2)
  {
    printf("%d %d\n",path[i],path[i+1]);
  }
  //printf("%d %d\n",path[len-1],path[0]);
  return true;
}

void dfs(int n)
{
  for(int i=1;i<=50;i++)
  {
    if(graph[n][i])
    {
      graph[n][i]--;
      //graph[i][n]=graph[n][i];
      graph[i][n]--;
      du[n]--;
      du[i]--;
      //path[len++]=n;
      dfs(i);     
      path[len++]=i;
      path[len++]=n;                    
    }
  }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值