ZOJ3732 Graph Reconstruction Havel-Hakimi定理
分析:
给定一个非负整数序列{dn},若存在一个无向图使得图中各点的度与此序列一一对应,则称此序列可图化。
进一步,若图为简单图,则称此序列可简单图化 (来自百度百科)
可简单图化的判定可以用Havel-Hakimi定理,然后简述 Havel-Hakimi定理
Havel-Hakimi定理的过程:
1,按度数排序。
2,选取度数最大的点,如果该点度数为0,结束,有解
3,每次选一个度数最大的点,然后将后面的点的度数依次减1,表示该顶点和相应的顶点有边相连,
如果有点的度数减到负数,结束,无解。
4,重复进行步骤1
然后这个题不只是判定有没有解,还需要判断是否有多解
“个人认为此题的难点在于多解时要输出两个。在Havel-Hakimi定理的构造过程中,如果把某两个点“互换”,那么就可以构造出多解的,
那么什么样的两个点才可以互换呢,比如我现在已经排完序,要减的度数序列一直到p,这时,如果p+1的点的度数和p是相同的,
那么p位置和p+1是可以"互换“的,连这两个点中的哪一个都不会影响结果。”(该段文字引用网上某大牛的想法)
所以通过判断p和p+1是否相等,就可以判断是否多解
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<cstring> #include<queue> #include<cstdlib> #include<algorithm> #include<vector> #include<cmath> using namespace std; typedef long long LL; const int N=1e2+5; const int INF=0x3f3f3f3f; int a[N],d[N],c[N],n; bool cmp(int x,int y) { return c[x]>c[y]; } bool check() { for(int i=1;i<=n;++i)c[i]=d[i]; for(int i=1; i<=n; ++i) { sort(a+1,a+1+n,cmp); if(!c[a[1]])break; for(int j=2,cnt=1; j<=n&&cnt<=c[a[1]]; ++j,++cnt) { --c[a[j]]; if(c[a[j]]<0)return 0; } c[a[1]]=0; } return 1; } struct Edge { int u,v; } x[N*N]; bool checkmul() { for(int i=1; i<=n; ++i)c[i]=d[i]; for(int i=1; i<=n; ++i) { sort(a+1,a+1+n,cmp); if(!c[a[1]])break; int k=c[a[1]]+2; if(k<=n&&c[a[k]]==c[a[k-1]])return 1; for(int j=2,cnt=1; j<=n&&cnt<=c[a[1]]; ++j,++cnt) --c[a[j]]; c[a[1]]=0; } return 0; } void print() { int tot=0; for(int i=1; i<=n; ++i)c[i]=d[i]; for(int i=1; i<=n; ++i) { sort(a+1,a+1+n,cmp); if(!c[a[1]])break; for(int j=2,cnt=1; j<=n&&cnt<=c[a[1]]; ++j,++cnt) { --c[a[j]]; ++tot; x[tot].u=a[1],x[tot].v=a[j]; } c[a[1]]=0; } for(int i=1; i<=tot; ++i) { printf("%d",x[i].u); if(i<tot)printf(" "); } printf("\n"); for(int i=1; i<=tot; ++i) { printf("%d",x[i].v); if(i<tot)printf(" "); } printf("\n"); } int main() { while(~scanf("%d",&n)) { int sum=0; for(int i=1; i<=n; ++i) { scanf("%d",&d[i]); sum+=d[i]; a[i]=i; } if(!check()) { printf("IMPOSSIBLE\n"); continue; } if(checkmul()) { printf("MULTIPLE\n"); printf("%d %d\n",n,sum/2); print(); printf("%d %d\n",n,sum/2); for(int i=1; i<=n; ++i)c[i]=d[i]; int tot=0; bool flag=1; for(int i=1; i<=n; ++i) { sort(a+1,a+1+n,cmp); if(!c[a[1]])break; if(flag) { int k=c[a[1]]+2; if(k<=n&&c[a[k]]==c[a[k-1]]) swap(a[k],a[k-1]),flag=0; } for(int j=2,cnt=1; j<=n&&cnt<=c[a[1]]; ++j,++cnt) { --c[a[j]]; ++tot; x[tot].u=a[1],x[tot].v=a[j]; } c[a[1]]=0; } for(int i=1; i<=tot; ++i) { printf("%d",x[i].u); if(i<tot)printf(" "); } printf("\n"); for(int i=1; i<=tot; ++i) { printf("%d",x[i].v); if(i<tot)printf(" "); } printf("\n"); } else { printf("UNIQUE\n"); printf("%d %d\n",n,sum/2); print(); } } return 0; }