Degree Sequence of Graph G(Havel-Hakimi定理)

原题链接

Problem Description

Wang Haiyang is a strong and optimistic Chinese youngster. Although born and brought up in the northern inland city Harbin, he has deep love and yearns for the boundless oceans. After graduation, he came to a coastal city and got a job in a marine transportation company. There, he held a position as a navigator in a freighter and began his new life.
The cargo vessel, Wang Haiyang worked on, sails among 6 ports between which exist 9 routes. At the first sight of his navigation chart, the 6 ports and 9 routes on it reminded him of Graph Theory that he studied in class at university. In the way that Leonhard Euler solved The Seven Bridges of Knoigsberg, Wang Haiyang regarded the navigation chart as a graph of Graph Theory. He considered the 6 ports as 6 nodes and 9 routes as 9 edges of the graph. The graph is illustrated as below.在这里插入图片描述

According to Graph Theory, the number of edges related to a node is defined as Degree number of this node.
Wang Haiyang looked at the graph and thought, If arranged, the Degree numbers of all nodes of graph G can form such a sequence: 4, 4, 3,3,2,2, which is called the degree sequence of the graph. Of course, the degree sequence of any simple graph (according to Graph Theory, a graph without any parallel edge or ring is a simple graph) is a non-negative integer sequence?
Wang Haiyang is a thoughtful person and tends to think deeply over any scientific problem that grabs his interest. So as usual, he also gave this problem further thought, As we know, any a simple graph always corresponds with a non-negative integer sequence. But whether a non-negative integer sequence always corresponds with the degree sequence of a simple graph? That is, if given a non-negative integer sequence, are we sure that we can draw a simple graph according to it.?
Let’s put forward such a definition: provided that a non-negative integer sequence is the degree sequence of a graph without any parallel edge or ring, that is, a simple graph, the sequence is draw-possible, otherwise, non-draw-possible. Now the problem faced with Wang Haiyang is how to test whether a non-negative integer sequence is draw-possible or not. Since Wang Haiyang hasn’t studied Algorithm Design course, it is difficult for him to solve such a problem. Can you help him?

Input

The first line of input contains an integer T, indicates the number of test cases. In each case, there are n+1 numbers; first is an integer n (n<1000), which indicates there are n integers in the sequence; then follow n integers, which indicate the numbers of the degree sequence.

Output

For each case, the answer should be "yes"or “no” indicating this case is “draw-possible” or “non-draw-possible”

Sample Input

2
6 4 4 3 3 2 2
4 2 1 1 1

Sample Output

yes
no

单词:

  • inland city 内陆城市
  • coastal city 沿海城市
  • yearn 向往
  • boundless 无限的
  • marine transportation company 海运公司
  • navigator 航海家
  • freighter 货轮
  • sail = navigate 航行
  • port = harbor 港口
  • At the first sight of 乍一看
  • sequence 顺序,序列
  • degree sequence 度序列
  • corresponds with 对应于

原文翻译:

王海洋是一个坚强而乐观的中国年轻人。 尽管他在北方内陆城市哈尔滨出生并长大,但他对无限的海洋有着深切的爱慕和向往。毕业后,他来到一个沿海城市,并在一家海上运输公司找到了工作。 在那儿,他担任一艘货轮的航海家,开始了他的新生活。
王海洋的货轮在6个港口之间航行,这些港口之间存在9条路线。乍一看他的导航图上的6个端口和9条路线,使他想起了他在大学学习的图论。他将6个港口视为6个节点,将9条路线视为图的9条边。
根据图论,将与节点有关的边数定义为该节点的度数。
王海洋看了看图,想到了如果安排得好,图G的所有节点的度数就可以形成这样的序列:4、4、3、3、2、2,称为图的度数序列。 当然,任何简单图的度序列(根据图论,没有任何平行边或环的图都是简单图)是非负整数序列。
王海洋是一个有思想的人,并且倾向于对任何引起他兴趣的科学问题进行深入思考。因此,像往常一样,他还对这个问题作了进一步的思考:众所周知,任何一个简单的图总是与一个非负整数序列相对应。但是,一个非负整数序列是否始终对应于一个简单图的度数序列?也就是说,如果给定一个非负整数序列,我们是否可以根据它绘制一个简单的图?
让我们提出这样一个定义:假设一个非负整数序列是没有任何平行边或环的图(即简单图)的度数序列,则该序列是可图的,否则是不可图的 。 现在,王海洋所面临的问题是如何测试非负整数序列是否可图。 由于王海洋没有学习算法设计课程,因此他很难解决这个问题。 你能帮他吗?

题意分析:

判断一个序列是否可图,即可图性判定。

Havel-Hakimi定理及相关概念介绍:

  • 度序列:若把图G所有顶点的度数(入度 + 出度)排成一个序列S,则称S为图G的度序列。
  • 序列是可图的:一个非负整数组成的有限序列如果是某个无向图(图可以连通,也可以不连通)的度序列,则称该序列是可图的。
  • 定理主要用来判定一个给定的序列是否是可图化的(可图:按照该度数序列,可以画出一个无向图)
  • Havel-Hakimi定理的判定过程:
    对当前数列S排序,使其呈递减(非递增)
    删除S[1],从S[2]开始对其后S[1]个数字,每个数字都进行减1操作
    每次减1操作执行完毕,需要重新对序列进行递减排序
    循环以上步骤
    在某次循环中当前序列出现负数即该序列不可图
    在某次循环中当前序列全为0即该序列可图

Havel-Hakimi算法核心代码:

bool Havel_Hakimi(int nums[])
{
    for(int i = 0;i < n;i++)
	{  
		sort(nums + i,nums + n,cmp);//每次都需要从第i个元素开始非递增排序 
		if(i + nums[i] >= n) return false;//若第i个元素 + nums[i]的值超过原数组长度,那么将溢出
		for(int j = i + 1; j <= i + nums[i];j++)
		{  
			nums[j]--;//范围内的数字每次减1  
			if(nums[j] < 0) return false;  
		}  
	}  
    if(nums[n - 1] != 0) return false;  
    return true;  
}

样例解释:

第一个样例

  • 6 4 4 3 3 2 2(已呈递减)
  • 3 3 2 2 1 1 (删除上个序列中的第一个数6,并对第一个数后面的6个数字进行减1操作得到3 3 2 2 1 1)
  • 2 1 1 1 1 (删除上个序列中的第一个数3,对第一个数后面的3个数字进行减1操作得到2 1 1 1 1)
  • 0 0 1 1(删除上个序列中的第一个数2,对第一个数后面的2个数字进行减1操作得到0 0 1 1)
  • 1 1(前一个序列中的0可以直接忽略)
  • 0 0(删除上个序列中的第一个数1,对第一个数后面的1个数字进行减1操作得到0 0)
  • 故第一个样例可图,输出yes

第二个样例

  • 4 2 1 1 1
  • 1 0 0 0
  • -1 0 0
  • 出现负数,故第二个序列不可图,输出no

AC代码:

没有使用奇偶性剪枝(124ms)
在这里插入图片描述

#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

int nums[1010];
int n;

bool cmp(int a,int b)//从大到小排序
{
    return a > b;
}

//Havel-Hakimi核心代码
bool Havel_Hakimi(int nums[])
{
    for(int i = 0;i < n;i ++)
	{  
		sort(nums + i,nums + n,cmp);
		if(i + nums[i] >= n) return false;
		for(int j = i + 1; j <= i + nums[i];j++)
		{  
			nums[j]--;  
			if(nums[j] < 0) return false;  
		}  
	}  
    if(nums[n - 1] != 0) return false;  
    return true;  
}

int main()
{
    int c;
    cin >> c;//多组数据
    
    while (c--)
    {
        memset(nums,0,sizeof nums);//初始化
        
        cin >> n;
        for (int i = 0;i < n;i++) cin >> nums[i];
        
        if (Havel_Hakimi(nums)) cout << "yes" << endl;//数组中的数全为0且无负数,说明序列可图
        else cout << "no" << endl;
    }
    return 0;
}

参考代码:

(没套用代码模板,整了一天也没AC)

#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

int flag = 1;//flag = 1说明序列可图,flag = 0说明序列不可图
int nums[1010];

bool cmp(int a,int b)//从大到小排序
{
    return a > b;
}

int main()
{
    int c;
    cin >> c;//多组数据
    
    while (c--)
    {
        memset(nums,0,sizeof nums);//初始化
        
        int n;
        cin >> n;
        for (int i = 0;i < n;i++) cin >> nums[i];
        
        sort(nums,nums + n,cmp);//排序
        
        //Havel-Hakimi
        int temp;
        for (int j = 0;j < n;j++)
        {
            temp = nums[j];//temp暂存序列中的第1个数
            nums[j] = 0;//保存后将第1个数改为0
            int u = j + 1;//用来遍历数字使其减1
            for (int k = 1;k <= temp;k++)//当前序列中第一个非0数是几,执行几次循环
            {
                nums[u++] -= 1;
                if (nums[u - 1] < 0)//减为负数
                {
                    flag = 0;
                    break;
                }
            }
            
            sort(nums + j + 1,nums + n,cmp);//删除1个数字后的序列,从删除数字的下个数字开始重新排序
        }
        
        for (int i = 0;i < n;i++)//遍历有无不为0的数 
        {
            if (nums[i] != 0) flag = 0;
        }
        if (flag) cout << "yes" << endl;//数组中的数全为0且无负数,说明序列可图
        else cout << "no" << endl;
    }
    return 0;
}

AC代码的优化:

奇偶性剪枝(46ms)

#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

int nums[1010];
int n,sum;

bool cmp(int a,int b)
{
    return a > b;//non-increasing sort
}

//Havel-Hakimi algorithm
bool Havel_Hakimi(int nums[])
{
    for (int i = 0;i < n;i++)
    {
        sort(nums + i,nums + n,cmp);
        if (i + nums[i] >= n) return false;
        for (int j = i + 1;j <= i + nums[i];j++)
        {
            nums[j]--;
            if (nums[j] < 0) return false;
        }
    }
    if (nums[n - 1] != 0) return false;
    return true;
}

int main()
{
    int c;
    scanf("%d",&c);
    
    while (c--)
    {
        memset(nums,0,sizeof nums);//init
        
        scanf("%d",&n);
        
        sum = 0;
        for (int i = 0;i < n;i++)
        {
            scanf("%d",&nums[i]);
            sum += nums[i];
        }
        
        /*
            odd & 1 = 1
            even & 1 = 0
            奇偶性剪枝:若所有的度加起来为奇数,则不可图
        */
        if (sum & 1)   
        {
            printf("no\n");
            continue;
        }
        
        if (Havel_Hakimi(nums)) printf("yes\n");
        else printf("no\n");
    }
    return 0;
}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值