2012-05-31 17:12:11
原创作品,允许转载,转载时请务必以超链接形式标明文章
原始出处 、作者信息和本声明。否则将追究法律责任。
http://sbp810050504.blog.51cto.com/2799422/883904
我一直想写一些关于图论学习的收获。一直由于这样或者那样的原因都没有开始。无论如何,现在开始吧!
那么到底什么是图呢?我们这里说的图当然不是像照片一样的东东。最权威的定义:图=顶点集合+边集合。换言之,凡是能抽象成点集合和边集合的东西都是图。比如:中国地图。地图上的城市是一个个的点,而任意两个相邻城市之间有路。那么地图就是很直观的一种图。为了更方便的表示,我们引入了英语单词。Vertext(顶点),Edge(边),Graph(图)。不管什么图,就可以用G<V,E>表示了。
地图是一种图,中国的七大水系网也是一种图。很多湖泊可以视为点,而长江、黄河这些可以看作边。由于水流基本上是单向的(当然也有倒流的情况,这里我们理想化就忽略了),所以,像这样:边有方向的图我们称为有方向的图(长江水只能从喜马拉雅流向东海,而绝对不会反过来的)简称有向图。如果边没有方向,那当然称为无向图了。
我们还是回到地图。都说条条大路通北京(你说罗马也没错),那么有到底有多少条路到北京呢?在图论里,把这样连接到一个点的边的数量(比如连接到北京的路)称为:度!
对于图的所有顶点,我们可以统计出每个顶点的度。像这样的一串数字,我们称之为:度序列。那么反过来,给定一个序列,能否判断这个序列是可图的呢?这里有一个定理:Havel-Hakimi定理可以用来判定一个序列是否可图。Poj1659就是用Havel-Hakimi解决的。
Havel-Hakimi定理的内容可百度之。
Havel-Hakimi定理很容易理解:
三步走就可以了:
比如序列:4 7 7 3 3 3 2 1
下标
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
8
|
值
|
4
|
7
|
7
|
3
|
3
|
3
|
2
|
1
|
第一步:把序列按降序排序。
下标
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
8
|
值
|
7
|
7
|
4
|
3
|
3
|
3
|
2
|
1
|
第二步:删除第一个数7。序列变成
下标
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
值
|
7
|
4
|
3
|
3
|
3
|
2
|
1
|
第三步:从头开始,数7个数,也就是下标:[1,7]把[1,7]区间里的值都减1
由于第一个数已经删除,那么序列变成这样的了:
下标
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
值
|
6
|
3
|
2
|
2
|
2
|
1
|
0
|
然后:
重复第一步:排序。
重复第二步:删除第一个数6
重复第三步:从头开始数6个数:也就是下标【1,6】,把区间【1,6】中的数删除。序列变成:
下标
|
1
|
2
|
3
|
4
|
5
|
6
|
值
|
2
|
1
|
1
|
1
|
0
|
-1
|
由于已经出现了-1,而一个点的边数(度)不可能为负数。所以,我们就可以判定序列无法构成一个图,所以此序列是不可图的。
下面再举一个例子:
已经排序:
5
|
4
|
3
|
3
|
2
|
2
|
2
|
1
|
1
|
1.
|
删除第一个数5:
4
|
3
|
3
|
2
|
2
|
2
|
1
|
1
|
1.
|
把前面5个数减1:
3
|
2
|
2
|
1
|
1
|
2
|
1
|
1
|
1.
|
排序:
3
|
2
|
2
|
2
|
1
|
1
|
1
|
1
|
1.
|
删除第一个数3:
2
|
2
|
2
|
1
|
1
|
1
|
1
|
1.
|
把前面3个数减1:
1
|
1
|
1
|
1
|
1
|
1
|
1
|
1.
|
排序:
1
|
1
|
1
|
1
|
1
|
1
|
1
|
1.
|
删除第一个数1:
1
|
1
|
1
|
1
|
1
|
1
|
1.
|
把前面1个数减1:
0
|
1
|
1
|
1
|
1
|
1
|
1.
|
排序:
1
|
1
|
1
|
1
|
1
|
1
|
0
|
删除第一个数1:
1
|
1
|
1
|
1
|
1
|
0
|
把前面1个数减1:
0
|
1
|
1
|
1
|
1
|
0
|
排序:
1
|
1
|
1
|
1
|
0
|
0
|
依此类推:到最后只剩下:
0
|
0
|
0
|
0
|
由此判断该序列是可图的。
- /*
- *题目大意:
- *给出一个图的每个点的度的序列,求能否构成一个简单图,如果能构出简单图,则输出图的邻接矩阵;
- *
- *算法思想:
- *Havel定理的应用;
- *给定一个非负整数序列{dn},若存在一个无向图使得图中各点的度与此序列一一对应,则称此序列可图化;
- *若图为简单图,则称此序列可简单图化;
- *
- *可图化的判定:
- *d1+d2+……dn==0(mod 2);
- *
- *处理过程:
- *每次处理度数最大的点,设其度数为d则将他与度数最大的d个点(不含自己)个连一条边(若该点度数大于0),更新度数;
- *重复上面操作,如果最后恰好所有度数为0则为可行方案;
- **/
- #include<iostream>
- #include<cstdio>
- #include<cstdlib>
- #include<algorithm>
- #include<cstring>
- using namespace std;
- const int N=20;
- int map[N][N];
- int n;
- struct node
- {
- int degree;
- int id;
- } a[N];
- bool cmp(node x , node y)
- {
- return x.degree>y.degree;
- }
- int main()
- {
- //freopen("C:\\Users\\Administrator\\Desktop\\kd.txt","r",stdin);
- int t1;
- scanf("%d",&t1);
- int t2=0;
- while(t1--)
- {
- if(t2)
- puts("");
- t2++;
- memset(map,0,sizeof(map));
- scanf("%d",&n);
- int sum=0;
- for(int i=0; i<n; i++)
- {
- scanf("%d",&a[i].degree);
- a[i].id=i;
- sum+=a[i].degree;
- }
- if(sum%2)
- {
- puts("NO");
- continue;
- }
- int flag=0;
- for(int i=0; i<n; i++)
- {
- sort(a,a+n,cmp);
- if(a[0].degree==0)
- {
- flag=1;
- break;
- }
- for(int j=0; j<a[0].degree; j++)
- {
- a[j+1].degree--;
- int x=a[0].id;
- int y=a[j+1].id;
- map[x][y]=map[y][x]=1;
- if(a[j+1].degree<0)
- {
- flag=2;
- break;
- }
- }
- a[0].degree=0;
- if(flag==2)
- break;
- }
- if(flag==1)
- {
- puts("YES");
- for(int i=0; i<n; i++)
- {
- int j=0;
- for(; j<n-1; j++)
- printf("%d ",map[i][j]);
- printf("%d\n",map[i][j]);
- }
- }
- else
- puts("NO");
- }
- return 0;
- }
1,Havel-Hakimi定理主要用来判定一个给定的序列是否是可图的。
2,首先介绍一下度序列:若把图 G 所有顶点的度数排成一个序列 S,则称 S 为图 G 的度序列。
3,一个非负整数组成的有限序列如果是某个无向图的序列,则称该序列是可图的。
4,判定过程:(1)对当前数列排序,使其呈递减,(2)从S【2】开始对其后S【1】个数字-1,(3)一直循环直到当前序列出现负数(即不是可图的情况)或者当前序列全为0 (可图)时退出。
5,举例:序列S:7,7,4,3,3,3,2,1 删除序列S的首项 7 ,对其后的7项每项减1,得到:6,3,2,2,2,1,0,继续删除序列的首项6,对其后的6项每项减1,得到:2,1,1,1,0,-1,到这一步出现了负数,因此该序列是不可图的。
6,应用:http://poj.org/problem?id=1659
题意很简单,分析之后会发现其实本题的意思就是想问:给定一个非负数序列,问是不是一个可图的序列,即能不能根据这个序列构造一个图,有2种不合理的情况:(1)某次对剩下序列排序后,最大的度数(设为d1)超过了剩下的顶点数;(2)对最大度数后面的d1个数各减1后,出现了负数。
贴下代码:
- #include<stdio.h>
- #include<string.h>
- #include<algorithm>
- using namespace std;
- struct node
- {
- int num,e;
- }x[15];
- bool map[15][15];
- int cmp(node a,node b)
- {
- if(a.num==b.num)
- return a.e<b.e;
- return a.num>b.num;
- }
- int judge(int n)
- {
- int i,num,tmp;
- while(1){
- sort(x+1,x+n+1,cmp);
- if(!x[1].num)
- return 1;//数组全为 0 的情况退出
- for(i=2;i<=x[1].num+1;i++){
- if(x[i].num>0){
- x[i].num--;
- map[x[1].e][x[i].e]=map[x[i].e][x[1].e]=1;
- }
- else
- return 0;
- }
- x[1].num=0;
- }
- }
- int main()
- {
- int n,t,i,j;
- bool flag;
- scanf("%d",&t);
- while(t--){
- scanf("%d",&n);
- for(i=1;i<=n;i++){
- scanf("%d",&x[i].num);
- x[i].e=i;
- }
- memset(map,0,sizeof(map));
- flag=judge(n);
- if(flag){
- printf("YES/n");
- for(i=1;i<=n;i++){
- for(j=1;j<=n;j++)
- printf(j==1?"%d":" %d",map[i][j]);
- printf("/n");
- }
- }
- else
- printf("NO/n");
- if(t)
- printf("/n");
- }
- return 0;
- }