PKU 2942 Knights of the Round Table - 无向图的块 判断奇圈

题目大意:

N个骑士某些骑士之间会有仇恨。骑士们开会时围坐在一个圆桌旁。一次会议能够举行,当且仅当没有相邻的两个骑士相互仇恨,且开会人数为大于2的奇数。若某个骑士任何会议都不能参加,那么就必须将它踢出。给出骑士之间的仇恨关系,问需要踢出多少个骑士。

分析

首先,求出无向图的块(即同一个块中没有割点)。块中的节点一定是可以组成环的。

可以证明,若块中存在一个奇圈,那么块中所有顶点都在一个奇圈上。

那么,对于每个块进行判定,若该块存在奇圈,则块中所有的骑士都不会被踢出。即可求解。

需要注意的是,同一个顶点可能出现在多个块当中。所以在计数时不能直接累加含有奇圈的块的顶点,而是要先标记再累加。

至于只有一个骑士的情况怎么处理,貌似题目没有这样的数据……

 

  1. /*
  2. PKU2942 Knights of the Round Table
  3. */
  4. #include <stdio.h>
  5. #include <memory.h>
  6. #define clr(a) memset(a,0,sizeof(a))
  7. #define MIN(a,b) ((a)>(b)?(b):(a))
  8. #define N 1005
  9. #define M 4000005
  10. typedef struct NS{ int j; struct NS *next; }Node;
  11. Node mem[M]; int memp;
  12. void addEdge(Node *e[],int i,int j){
  13.     Node *p=&mem[memp++]; p->j=j; p->next=e[i]; e[i]=p;
  14. }
  15. int anc[N],mark[N],deep[N],stack[N],sp,nblock;  //anc[] DFS中i可达最远祖先的深度
  16. int DFS_block(Node *e[],int i,int father,int dth,int cut[],Node *be[]){
  17.     int j,k,tofather=0,sons=0; Node *p;
  18.     stack[sp++]=i; mark[i]=1; anc[i]=deep[i]=dth;
  19.     
  20.     for(p=e[i];p!=NULL;p=p->next){
  21.         j=p->j;
  22.         if(mark[j]==1 && (j!=father||tofather)) anc[i]=MIN(anc[i],deep[j]);
  23.         if(mark[j]==0){
  24.             DFS_block(e,j,i,dth+1,cut,be);
  25.             anc[i]=MIN(anc[i],anc[j]); sons++;
  26.             if((father==-1&&sons>1)||(father!=-1&&anc[j]>=deep[i]))
  27.                 cut[i]=1;
  28.             if(anc[j]>=deep[i]) {
  29.                 addEdge(be,nblock,i);
  30.                 for(stack[sp]=-1;stack[sp]!=j;addEdge(be,nblock,stack[--sp]));
  31.                 nblock++;
  32.             }
  33.         }
  34.         if(j==father) tofather=1;
  35.     }
  36.     mark[i]=2;
  37. }
  38. /*
  39. 无向图的块
  40. 参数:
  41.     传入无向图邻接表e[], 返回无向图块的个数nblock
  42.     cut[]返回顶点i是否为割顶
  43.     be[]返回每个块所包含的顶点,be[k]之后是块k包含的所有顶点.
  44. */
  45. int Block(Node *e[],int n,int cut[],Node *be[]){
  46.     int i,j,k,f[N]; 
  47.     clr(mark); nblock=0; sp=0;
  48.     for(i=0;i<n;i++) if(mark[i]==0) DFS_block(e,i,-1,1,cut,be);
  49.     return nblock;
  50. }
  51. /**************************************/
  52. int DFS_judge(Node *e[],int i,int col,int in[],int color[]){
  53.     int j,k; Node *p;
  54.     color[i]=col;
  55.     for(p=e[i];p!=NULL;p=p->next){
  56.         j=p->j;
  57.         if(!in[j]) continue;
  58.         if(!color[j]){ if(DFS_judge(e,j,-col,in,color)) return 1;}
  59.         else {
  60.             if(color[j]==col) {
  61.                 return 1;
  62.             }
  63.         }
  64.     }
  65.     return 0;
  66. }
  67. void Judge(Node *e[],Node *block,int good[]){
  68.     int i,j,k; Node *p,*q;
  69.     int in[N],color[N];
  70.     clr(in); clr(color);
  71.     for(p=block;p!=NULL;p=p->next){
  72.         in[p->j]=1;
  73.     }
  74.     if(DFS_judge(e,block->j,1,in,color)){
  75.         for(p=block;p!=NULL;p=p->next) good[p->j]=1;
  76.     }
  77. }
  78. int n,m,w;
  79. Node *e[N];
  80. Node *be[N];
  81. int cut[N];
  82. int good[N],count;
  83. int hate[N][N];
  84. int main()
  85. {
  86.     int i,j,k;
  87.     
  88.     while(scanf("%d%d",&n,&w)!=EOF && n+w){
  89.         //init
  90.         memp=0; clr(e); clr(be); clr(hate); clr(cut);
  91.         //input
  92.         for(k=0;k<w;k++) {
  93.             scanf("%d%d",&i,&j); i--;j--;
  94.             hate[i][j]=hate[j][i]=1;
  95.         }
  96.         //create map
  97.         for(i=0;i<n;i++) for(j=i+1;j<n;j++) if(!hate[i][j]){
  98.             addEdge(e,i,j);
  99.             addEdge(e,j,i);
  100.         }
  101.         //block
  102.         m=Block(e,n,cut,be);
  103.         
  104.         //count
  105.         clr(good); count=0;
  106.         for(k=0;k<m;k++) Judge(e,be[k],good);
  107.         for(i=0;i<n;i++) if(good[i]) count++;
  108.         //output
  109.         printf("%d/n",n-count);
  110.     }
  111.     
  112.     return 0;
  113. }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值