POJ 1659 Frogs' Neighborhood(Havel方法)

说的复杂了是Havel方法, 实际上能通过分析找出规律

目给出n个池塘, 告诉每个池塘相邻池塘的个数, 问是否存在一种池塘的分布符合题目的要求

显然应该先处理最多的, 因为它与大部分池塘发生联系,  假设池塘i相邻的池塘有xi个, 则处理完池塘i之后, 有另外xi个池塘的相邻池塘数xj会减小1.

为了保证相邻池塘数多的池塘都能符合要求, 应该从池塘数多的池塘进行减1操作


另外给出Havel方法的证明

给定一个非负整数序列{dn},若存在一个无向图使得图中各点的度与此序列一一对应,则称此序 
列可图化。进一步,若图为简单图,则称此序列可简单图化    

可图化的判定:d1+d2+……dn=0(mod 2)。关于具体图的构造,我们可以简单地把奇数度的点配 
对,剩下的全部搞成自环。   

可简单图化的判定(Havel定理):把序列排成不增序,即d1>=d2>=……>=dn,则d可简单图化当且仅当 
d’={d2-1,d3-1,……d(d1+1)-1, d(d1+2),d(d1+3),……dn}可简单图化。简单的说,把d排序后, 
找出度最大的点(设度为d1),把它与度次大的d1个点之间连边,然后这个点就可以不管了,一直继续这 
个过程,直到建出完整的图,或出现负度等明显不合理的情况。 
对于一个给定的度序列,看能不能形成一个简单无向图。 

Havel算法的思想简单的说如下: 

(1)对序列从大到小进行排序。 
(2)设最大的度数为 t ,把最大的度数置0,然后把最大度数后(不包括自己)的t 个度数分别减1 
(意思就是把度数最大的点与后几个点进行连接) 
(3)如果序列中出现了负数,证明无法构成。如果序列全部变为0,证明能构成,跳出循环。前两 
点不出现,就跳回第一步! 

举例说明: 

4 4 3 3 2 2 
第二步后0 3 2 2 1 2 
排完续后3 2 2 2 1 0 
第二步后0 1 1 1 1 0 
排完续后1 1 1 1 0 0 
第二步后0 0 1 1 0 0 
排完续后1 1 0 0 0 0 
第二步后0 0 0 0 0 0 
全为0,能构成图,跳出! 


2 1 1 1 
第二步后0 0 0 1 
排完续后1 0 0 0 
第二步后0 -1 0 0 
出现负数,直接退出!


#include <iostream>
#include <cstring>
#include <fstream>
#include <algorithm>
using namespace std;
struct Node
{
    int xu, num;
} q[12];
int g[12][12];
int cmp(Node a, Node b)
{
    return a.num > b.num;
}
int main()
{
    int t, i, j, n, flag;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d", &n);
        flag = 0;
        for(i = 1; i <= n; i++)
        {
            scanf("%d", &q[i].num);
            q[i].xu = i;
        }
        memset(g, 0, sizeof(g));
        while(1)
        {
            sort(q+1, q+n+1, cmp);
            if(q[1].num == 0)
                break;
            for(i = 1; i <= q[1].num; i++)
            {
                q[1+i].num--;
                g[q[1].xu][q[1+i].xu] = g[q[1+i].xu][q[1].xu] = 1;
                if(q[1+i].num < 0)
                {
                    flag = 1;
                    break;
                }
            }
            if(flag == 1)
                break;
            q[1].num = 0;
        }
        if(flag)
            printf("NO\n");
        else
        {
            printf("YES\n");
            for(i=1; i<=n; i++)
                for(j=1; j<=n; j++)
                    printf("%d%c",g[i][j],j==n?'\n':' ');
        }
        printf("\n");
    }
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值