POJ 1659【Havel-Hakimi 定理】
Havel-Hakimi 定理:
一个非负整数组成的有限序列如果是某个无向图的序列,则称该序列是可图的。
例如,判断序列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,
到这一步出现了负数。由于图中不可能存在负度数的顶点,因此该序列不是可图的。
再举一个例子,判断序列s: 5, 4, 3, 3, 2, 2, 2, 1, 1, 1 是否是可图的。
删除序列s 的首项5,对其后的5 项每项减1,得到:3, 2, 2, 1, 1, 2, 1, 1, 1,
重新排序后为:3, 2, 2, 2, 1, 1, 1, 1, 1。
继续删除序列的首项3,对其后的3 项每项减1,得到:1, 1, 1, 1, 1, 1, 1, 1。
如此再陆续得到序列:1, 1, 1, 1, 1,1, 0;1, 1, 1, 1, 0, 0;1, 1, 0, 0, 0;0, 0, 0, 0。
由此可判定该序列是可图的。
Havel-Hakimi 定理实际上给出了根据一个序列s 构造图(或判定s 不是可图的)的方法:把序列s 按照非增顺序
排序以后,其顺序为d1, d2, …, dn;度数最大的顶点(设为v1),将它与度数次大的前d1 个顶点之间连边,然
后这个顶点就可以不管了,即在序列中删除首项d1,并把后面的d1个度数减1;再把剩下的序列重新按非增顺序排
序,按照上述过程连边;…;直到建出完整的图,或出现负度数等明显不合理的情况为止。
再回到本题:
Time Limit: 5000MS | Memory Limit: 10000K | |||
Total Submissions: 10443 | Accepted: 4340 | Special Judge |
Description
未名湖附近共有N个大小湖泊L1, L2, ..., Ln(其中包括未名湖),每个湖泊Li里住着一只青蛙Fi(1 ≤ i ≤ N)。如果湖泊Li和Lj之间有水路相连,则青蛙Fi和Fj互称为邻居。现在已知每只青蛙的邻居数目x1, x2, ..., xn,请你给出每两个湖泊之间的相连关系。
Input
第一行是测试数据的组数T(0 ≤ T ≤ 20)。每组数据包括两行,第一行是整数N(2 < N < 10),第二行是N个整数,x1, x2,..., xn(0 ≤ xi ≤ N)。
Output
对输入的每组测试数据,如果不存在可能的相连关系,输出"NO"。否则输出"YES",并用N×N的矩阵表示湖泊间的相邻关系,即如果湖泊i与湖泊j之间有水路相连,则第i行的第j个数字为1,否则为0。每两个数字之间输出一个空格。如果存在多种可能,只需给出一种符合条件的情形。相邻两组测试数据之间输出一个空行。
Sample Input
3 7 4 3 1 5 4 2 1 6 4 3 1 4 2 0 6 2 3 1 1 2 1
Sample Output
YES 0 1 0 1 1 0 1 1 0 0 1 1 0 0 0 0 0 1 0 0 0 1 1 1 0 1 1 0 1 1 0 1 0 1 0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 NO YES 0 1 0 0 1 0 1 0 0 1 1 0 0 0 0 0 0 1 0 1 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0
注意:由该定理得到的图是不唯一的。
题解:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <set> #include <map> #define INF using namespace std; const int maxn=105; int t,n; int e[maxn][maxn]; struct node{ int degree;//顶点的度数 int num;//顶点的编号 }a[maxn]; bool cmp(node a,node b){//度数由大到小排序 return a.degree>b.degree; } int main() { scanf("%d",&t); while(t--){ memset(e,0,sizeof e); scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d",&a[i].degree); a[i].num=i+1;//顶点编号由1-n } int flag=1;//旗帜变量 for(int i=0;i<n;i++){ sort(a,a+n,cmp); if(a[i].degree>n-i-1){//如果后序顶点的数量小于当前顶点的度数,则无法构成图 flag=0; break; } for(int j=i+1;j<=a[i].degree+i&&j<n;j++){ a[j].degree--; if(a[j].degree<0){ flag=0; break; } e[a[i].num][a[j].num]=e[a[j].num][a[i].num]=1;//连边 } } if(flag){ printf("YES\n"); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ printf("%d",e[i][j]); if(j!=n) printf(" "); } printf("\n"); } }else printf("NO\n"); printf("\n"); } return 0; }