cdoj913-握手 【Havel定理】
http://acm.uestc.edu.cn/#/problem/show/913
握手
Time Limit: 2000/1000MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others)
一群人参加了一次聚会,其中有一些人是好朋友。一对朋友见面后握手且仅握一次手,并且每个人不会和自己握手(废话!)。现在告诉你每个人一共握了几次手,请你判断是否存在一种朋友关系满足每个人的握手数。
Input
输入多组数据,第一行一个数T,表述数据组数。每组数据第一行输入一个数n,表示有n个人参加了聚会,下一行有n个数,di到dn ,di表示第i个人的握手数。 (1≤n≤105 ,输入的所有d之和不超过5×105)
Output
存在这种朋友关系输出YES
,反之NO
。
Sample input and output
Sample Input | Sample Output |
---|---|
3 3 0 1 1 3 2 2 2 3 1 1 1 |
YES YES NO |
题解:用Havel定理解即可。
握手定理:任意图所有顶点度数之和必为偶数。
度序列:V(G)={v1,v2,....vn},称序列 {d(v1),d(v2),....d(vn)}为度序列。
一个正整数序列(d1,d2,.....,dn)是度序列当且仅当。
Havel定理:
一个序列:
是简单图的度序列当且仅当:
算法流程:
设序列有n个元素,d1,d2,....dn
1、若序列中出现负数则无解,若序列全为为0则有解,否则转2。
2、取出序列中最大值dmax,若dmax大于n-1,无解退出。否则取出剩下n-1个元素中前dmax大的dmax个元素,把这些元素依次减1后放回序列中,dmax舍弃,n=n-1。
代码:
1 #include <fstream> 2 #include <iostream> 3 #include <cstdio> 4 #include <cstring> 5 #include <queue> 6 7 using namespace std; 8 9 const int N=100005; 10 priority_queue<int> p; 11 int n; 12 int a[N],t[N]; 13 bool b; 14 15 int main() 16 { 17 //freopen("D:\\input.in","r",stdin); 18 //freopen("D:\\output.out","w",stdout); 19 int T,cnt; 20 scanf("%d",&T); 21 while(T--){ 22 cnt=0; 23 b=1; 24 scanf("%d",&n); 25 for(int i=0;i<n;i++){ 26 scanf("%d",&a[i]); 27 cnt+=a[i]; 28 } 29 if(cnt&1) puts("NO"); 30 else{ 31 while(!p.empty()) p.pop(); 32 for(int i=0;i<n;i++) p.push(a[i]); 33 while(!p.empty()){ 34 cnt=p.top(); 35 p.pop(); 36 if(cnt>=n){ 37 b=0; 38 break; 39 }else if(cnt==0){ 40 break; 41 } 42 if(p.size()<cnt){ 43 b=0; 44 break; 45 }else{ 46 for(int i=0;i<cnt;i++){ 47 t[i]=p.top()-1; 48 p.pop(); 49 } 50 if(t[cnt-1]<0){ 51 b=0; 52 break; 53 } 54 for(int i=0;i<cnt;i++) 55 if(t[i]) p.push(t[i]); 56 n--; 57 } 58 } 59 if(b) puts("YES"); 60 else puts("NO"); 61 } 62 } 63 return 0; 64 }