ore性质及其证明应用

来源:互联网 发布:肯德基 麦当劳 知乎 编辑:程序博客网 时间:2024/05/16 09:02

122. The book

time limit per test: 0.5 sec. 
memory limit per test: 4096 KB

There is a group of N (2<=N<=1000) people which are numbered 1 through N, and everyone of them has not less than [ (N+1) / 2 ] friends. A man with number 1 has the book, which others want to read.Write the program which finds a way of transferring the book so that it will visit every man only once, passing from the friend to the friend, and, at last, has come back to the owner.Note: if A is a friend of B then B is a friend of A. 

Input

First line of input contains number N. Next N lines contain information about friendships. (i+1)-th line of input contains a list of friends of i-th man.

Output

If there is no solution then your program must output 'No solution'.   Else your program must output exactly N+1 number: this sequence should begin and should come to end by number 1, any two neighbours in it should be friends, and any two elements in it, except for the first and last, should not repeat. 

Sample Input

42 31 41 42 3

Sample Output

1 3 4 2 1

 

题目大意:给出一幅满足Ore性质的图,保证图中的每个顶点都至少与(n+1)/ 2个点相邻,求出这幅图中的哈密顿回路

题解:这道题我一开始我也不知道怎么去做,于是从网上看到说《组合数学》这书里面有证明,我就去研究了一下,发现很不错。

该算法都源于一个性质,这个性质就是Ore性质:对于图G中,任意两个不相邻的顶点,他们的度数之和大于等于顶点数,即deg(u)+deg(v)>=n

1、首先通过Ore性质来证明这图G是个连通图。

证明:

如果该图G不是个连通图,那么存在至少两个连通分量,设这两个连通分量分别为X,Y,各个连通分量的顶点数位为r,s,r+s=n

因此连通分量X中度数最多的顶点的度最大为r-1,连通分量Y中度数最多的顶点的度最大为s-1,

那么对于两个连通分量的点(任意两个点不相邻),它们的度数之和最大为r+s-2<n,因此不满足Ore性质,所以这个图是连通图。

2、引出算法,通过Ore性质证明该算法的正确性

算法框架我写在程序里了,接下来就证明一下

证明算法框架第二步的正确性:

由于我们已经构造了一个极大链,已经无法继续扩展了,可知该链上任意两点都可互相到达,

此时链头顶点的所有邻接点都在链上,链尾顶点的所有邻接点都在链上,

如果链头顶点的邻接顶点不在链上,那么显然我们可以通过该链头顶点对链进行扩展

同理,如果链尾顶点的邻接顶点不在链上,我们也可以通过该链尾顶点对链进行扩展

我们可以找到一个链上一个点K,使得它与链尾顶点在图中相邻,并且保证链上顶点K-1与链头元素相邻

(假设链上有m个顶点,链头顶点有r个相邻顶点,链尾元素有s个相邻顶点,如果不存在K-1与链头元素相邻,

那么r <= m - 1 - s,此时r+s  <= m-1 <=n-1,此时他们不满足Ore性质,性质得证)

于是我们可以通过吧链头元素连向顶点K+1,链尾元素连向K,得到一个环 

下图可以说明,图中箭头方向路径为一个环

  1. /* 
  2.  *NAME:The Book 
  3.  *LANG:C++ 
  4.  *SOURCE:sgu122 
  5.  *METHOD:满足ORE条件的哈密顿回路构造. 
  6.  *1、任意选取一个点,从这个点向左或者向右一直扩展,直到无法继续扩展,形成了一条链 
  7.  *2、可以选取这条链中的某一个点,把从这个点开始到尾部的一泡点全部置换一下,就可以形成一个环 
  8.  *3、如果该环中点数满足N个,那么输出,否则从未标记的点里面找一个能接在环中点上的,拉出来,破环成链,返回第二步 
  9.  *TIMES:8 
  10.  */  
  11. #include <cstdio>  
  12. #include <cstdlib>  
  13. #include <cstring>  
  14. #include <string>  
  15. #include <iostream>  
  16. using namespace std;  
  17. const int mn =1100;  
  18. int n,k,i,j,listNum;  
  19. int L[mn],R[mn],head,tail;  
  20. bool adj[mn][mn] ={0};  
  21. bool visit[mn];  
  22. string s;  
  23. void reverse(){//checked  
  24.     int tmp[mn],t = head,num=1;  
  25.     tmp[num]=head;  
  26.     while (t!=tail){  
  27.     t=R[t];tmp[++num]=t;  
  28.     }tmp[++num]=tail;  
  29.     if (adj[head][tail]){  
  30.     R[tail] = head;L[head] = tail;  
  31.     return ;  
  32.     }  
  33.     else for (int i=2;i<num-1;++i)  
  34.          if (adj[head][tmp[i+1]] && adj[tail][tmp[i]]){  
  35.          L[head] = R[head];R[head] = tmp[i+1];  
  36.          L[tmp[i+1]] = head;R[tail] = tmp[i];  
  37.          R[tmp[i]]=L[tmp[i]];L[tmp[i]] = tail;  
  38.          for (int j = i-1;j>=2;--j){  
  39.              int p = L[tmp[ j ]];  
  40.              L[tmp[ j ]] = R[tmp[ j ]];R[tmp[ j ]]=p;  
  41.          }  
  42.          return;  
  43.          }  
  44. }  
  45. void add(){  
  46.     int tmp[mn],t = head,num=1;  
  47.     tmp[num]=head;  
  48.     while (t!=tail){  
  49.     t=R[t];tmp[++num]=t;  
  50.     }tmp[++num]=tail;int pos;  
  51.     for (int i=1;i<=n;++i)  
  52.     if (visit[i]){pos = i;visit[i]=false;break;}  
  53.     for (int i =1;i<=num;++i)  
  54.     if (adj[tmp[i]][pos]){  
  55.         head = R[tmp[i]];tail = pos;  
  56.         R[tmp[i]] = pos;  
  57.         L[pos] = tmp[i];  
  58.         R[pos] = 0;  
  59.         listNum++;  
  60.         return;  
  61.     }  
  62. }  
  63. int findHead(){//checked  
  64.     for (int i=1;i<=n;++i)  
  65.     if (visit[i] && adj[head][i]){  
  66.         L[head] = i;R[i]=head;L[i]=0;visit[i]=false;  
  67.         return i;  
  68.     }  
  69.     return 0;  
  70. }  
  71. int findTail(){//checked  
  72.     for (int i=1;i<=n;++i)  
  73.     if (visit[i] && adj[tail][i]){  
  74.         R[tail] = i;L[i]=tail;R[i]=0;visit[i]=false;  
  75.         return i;  
  76.     }  
  77.     return 0;  
  78. }  
  79. void print(){  
  80.     int tmp[mn],t = 1,num=1;  
  81.     tmp[num]=1;  
  82.     while (R[t]!=1){  
  83.     t=R[t];tmp[++num]=t;  
  84.     }  
  85.     for (int i=1;i<=num;++i) cout << tmp[i] << " " ;  
  86.     cout << 1 << " " << endl;  
  87. }  
  88. void insert(){int v;//checked  
  89.     //左右两端进行不断的扩展  
  90.     while (v=findHead()) {head = v;listNum ++;}  
  91.     while (v=findTail()) {tail = v;listNum ++;}  
  92.     reverse();//Let哈密顿路成为哈密顿回路  
  93.     if (listNum == n) print();//如果回路中元素满足N个,那么输出  
  94.     else{  
  95.     int tmp = listNum;  
  96.     add();  
  97.     insert();  
  98.     }  
  99. }  
  100. void init(){//checked  
  101.     cin >> n;getchar();  
  102.     for (i = 1;i <= n;++i){  
  103.     getline(cin,s);k=0;  
  104.     for (int j=0;j<s.size();++j)  
  105.         if (s[j]!=' ') k=k*10+s[j]-'0';  
  106.         else {adj[i][k]=true;k=0;}  
  107.     if (k!=0){  
  108.         adj[i][k]=true;k=0;  
  109.     }  
  110.     }memset(visit,true,sizeof(visit));  
  111.     tail = head = listNum = 1;visit[1]=false;  
  112. }  
  113. int main(){  
  114.     init();  
  115.     insert();  
  116.     return 0;  
  117. }  
0 0
原创粉丝点击