poj 1659

题目描述:
未名湖附近共有 n 个大小湖泊 L 1 , L 2 , ..., L n (其中包括未名湖) ,每个湖泊L i 里住着一只青蛙
F i (1≤i≤n) 。如果湖泊 L i 和 L j 之间有水路相连,则青蛙 F i 和 F j 互称为邻居。现在已知每只青蛙的
邻居数目 x 1 , x 2 , ..., x n ,请你给出每两个湖泊之间的相连关系。
输入描述:
第一行是测试数据的组数t(0 ≤ t ≤ 20) 。每组数据包括两行,第一行是整数 n(2 ≤ n ≤ 10) ,
第二行是 n 个整数,x 1 , x 2 ,..., x n (0 ≤ xi < n) 。
输出描述:
对输入的每组测试数据,如果不存在可能的相连关系,输出"NO"。否则输出"YES",并用 n×n
的矩阵表示湖泊间的相邻关系,即如果湖泊 i 与湖泊 j 之间有水路相连,则第 i 行的第 j 个数字为
1,否则为 0。每两个数字之间输出一个空格。如果存在多种可能,只需给出一种符合条件的情形。

相邻两组测试数据之间输出一个空行。

样例输入:
2
7
4 3 1 5 4 2 1
6
4 3 1 4 2 0

样例输出:
YES
0 1 1 1 1 0 0
1 0 0 1 1 0 0
1 0 0 0 0 0 0
1 1 0 0 1 1 1
1 1 0 1 0 1 0
0 0 0 1 1 0 0
0 0 0 1 0 0 0


NO

 Havel - Hakimi 定理  

 由非负整数组成的非增序列 s: d 1 , d 2 , …, d n (n ≥ 2, d 1 ≥ 1)
是可图的,当且仅当序列
s 1 : d 2 – 1, d 3 – 1, …, d d1+1 – 1, d d1+2 , …, d n
是可图的。序列 s 1 中有 n-1 个非负整数,s 序列中 d 1 后的前 d 1 个度数(即 d 2 ~d d1+1 )减 1 后构成
s 1 中的前 d 1 个数。
例如,判断序列 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 按照非增顺序排序以后,其顺序为 d 1 , d 2 , …, d n ;度数最大的顶点(设为 v 1 ) ,将它与度数次大
的前 d 1 个顶点之间连边,然后这个顶点就可以不管了,即在序列中删除首项 d 1 ,并把后面的 d 1
个度数减 1;再把剩下的序列重新按非增顺序排序,按照上述过程连边;…;直到建出完整的图,
或出现负度数等明显不合理的情况为止。
例如, 对序列 s: 3, 3, 2, 2, 1, 1 构造图, 设度数从大到小的 6 个顶点为 v 1 ~v 6 。 首先 v 1 与v 2 、 v 3 、
v 4 连一条边,如图 (a);剩下的序列为 2, 1, 1, 1, 1。如果后面 4 个 1 对应顶点 v 3 、v 4 、v 5 、
v 6 ,则应该在 v 2 与 v 3 、v 2 与 v 4 之间连边,最后在 v 5 与v 6 之间连边,如图(b)。如果后面 4 个 1
对应顶点 v 5 、v 6 、v 3 、v 4 ,则应该在 v 2 与 v 5 、v 2 与 v 6 之间连边,最后在 v 3 与 v 4 之间连边,如图(c)
。可见,由同一个可图的序列构造出来的图不一定是唯一的。




#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define N 15
struct vertex
{
    int degree;
    int index;
}v[N];
int cmp(const void *a,const void *b)
{
    return ((vertex*)b)->degree-((vertex*)a)->degree;
}
int main()
{
    int r,k,p,q;
    int i,j;
    int d1;
    int t,n;
    int Edge[N][N],flag;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(i=0;i<n;i++){
            scanf("%d",&v[i].degree);
            v[i].index=i;
        }
        memset(Edge,0,sizeof(Edge));
        flag=1;
        for(k=0;k<n&&flag;k++){
            qsort(v+k,n-k,sizeof(vertex),cmp);
            i=v[k].index;
            d1=v[k].degree;
            if(d1>n-k-1) flag=0;
            for(r=1;r<=d1&&flag;r++){
                j=v[k+r].index;
                if(v[k+r].degree<=0) flag=0;
                v[k+r].degree--;
                Edge[i][j]=Edge[j][i]=1;
            }
        }
        if(flag){
            puts("YES");
            for(p=0;p<n;p++){
                for(q=0;q<n;q++){
                    if(q) printf(" ");
                    printf("%d",Edge[p][q]);
                }
                puts("");
            }
        }
        else puts("NO");
        if(t) puts("");
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值