VIJOS p1023 Victoria的舞会3 终于AC了!!!

图论一直以来都很弱,又不懂什么最小点基(实现方法)……AC的时候……真爽啊。。。。。

Victoria的舞会3 Victoria的舞会 系列  
   
   
 
编译通过...
测试数据 01:答案正确... 0ms
测试数据 02:答案正确... 0ms
测试数据 03:答案正确... 0ms
测试数据 04:答案正确... 0ms
测试数据 05:答案正确... 0ms
测试数据 06:答案正确... 0ms
测试数据 07:答案正确... 0ms
测试数据 08:答案正确... 0ms
测试数据 09:答案正确... 0ms
测试数据 10:答案正确... 0ms
-------------------------
Accepted 有效得分:100 有效耗时:0ms

hoho~

先把题贴一下:

描述 Description  
    Victoria是一位颇有成就的艺术家,他因油画作品《我爱北京天安门》闻名于世界。现在,他为了报答帮助他的同行们,准备开一个舞会。
  Victoria准备邀请n个已经确定的人,可是问题来了:
  这n个人每一个人都有一个小花名册,名册里面写着他能够通知到的人的名字。比如说在A的人名单里写了B,那么表示A能够通知到B;但是B的名单里不见的有A,也就是说B不见得通知到A。
  Victoria觉得需要确定自己需要通知多少个人m,能够实际将所有人n都通知到。并求出一种方案以确定m的最小值是多少。
  注意:自己的名单里面不会有自己的名字。Victoria可以自身通知到所有n个人。
   
   
 输入格式 Input Format 
  第一行一个数n。接下来n行,每i+1行表示编号为i的人的小花名册名单,名单以0结束。1<=n<=200。
   
   
 输出格式 Output Format 
  一个数,m。
   
   
 样例输入 Sample Input  
 

18
0
11 0
0
0
0
16 0
14 0
0
0
0
2 13 0
0
11 0
7 0
0
6 0
0
0

   
   
 样例输出 Sample Output  
  14
   
   
 时间限制 Time Limitation 
  各个测试点1s

貌似有一个类似模拟的办法,VIJOS上有人说过了。

以下是我苦思冥想出来的成果,没想到一遍就AC了。

1 把原有向图G中强连通分量缩为一点

    1.1 DFS(G),记录节点访问完成的顺序,也可以说是一次拓扑排序。

    1.2 反转图每一条弧得到新图Gt。

    1.3 按照1.1记录的顺序DFS(Gt),得到的森林里每一棵树都是一个强连通分量。(用并查集合并每一个访问到的节点,从而实现“缩”强连通分量)。得到一强连通分量为单位的图Gscc。

2 DFS(Gscc),再一次拓扑排序,记录强连通分量访问完成的顺序。

4 按照2记录的顺序DFS(Gscc),用并查集得到访问产生的森林。

5 统计最后森林中树的个数即可得到答案。

 

程序(K&R风格代码)有120+行的样子。。。

 

 

#include < stdio.h >
#include
< stdbool.h >
void  toposort( void );
void  topovisit( int  i);
void  reverse( void );
void  contract( void );
void  convisit( int  i);
void  contract2( void );
void  convisit2( int  i);
 
int  rank[ 201 ], set [ 201 ];

void  Makeset( int  x)
{
        
set[x]=x;
        rank[x]
=0;
}

int  Find( int  x)
{
        
if(set[x]!=x)
                
set[x]=Find(set[x]);
        
return set[x];
}

void  Link( int  x, int  y)
{
        
if(rank[x]>rank[y])
                
set[y]=x;
        
else{
                
set[x]=y;
                
if(rank[x]==rank[y]) rank[y]++;
        }

}

void  Union( int  x, int  y)
{
        Link(Find(x),Find(y));
}


int  n,topo[ 201 ],p; 
int  nc;
bool  gt[ 201 ][ 201 ],g[ 201 ][ 201 ],vis[ 201 ];
int  main( void )
{
        
int i,j,k;
        
int t,count=0;
        FILE 
*fin=stdin;
        fscanf(fin,
"%d ",&n);
        nc
=n;
        
for(i=0;i<n;i++){
                
while(1){
                        fscanf(fin,
"%d"&t);
                        
if(!t) break;
                        g[i][t
-1]=true;
                }

                Makeset(i);
        }

        toposort();
        reverse();
        contract();
        toposort();
        contract2();
        
for(i=0;i<n;i++if(set[i]==i) count++;
        printf(
"%d ",count);
        
return 0;
}

void  toposort( void )
{
        
int i;
        memset(vis,
0,sizeof(vis));
        p
=0;
        
for(i=0;i<n;i++if(!vis[i]) topovisit(i);
}

void  topovisit( int  i)
{
        
int j;
        vis[i]
=true;
        
for(j=0;j<n;j++){
                
if(g[i][j] && Find(i)!=Find(j) && !vis[j]){
                        topovisit(j);
                }

        }

        topo[p
++]=Find(i);
        
}

void  reverse( void )
{
        
int i,j;
        
for(i=0;i<n;i++)
                
for(j=0;j<n;j++)
                        
if(g[i][j]) gt[j][i]=true;
}

void  contract( void )
{
        
int j;
        memset(vis,
0,sizeof(vis));
        
for(j=0;j<n;j++if (!vis[topo[j]]) convisit(topo[j]);
}

void  convisit( int  i)
{
        
int j;
        vis[i]
=true;
        
for(j=0;j<n;j++){
                
if(gt[i][j] && !vis[j]){
                        Union(i,j);
                        convisit(j);
                }

        }

}

void  contract2( void )
{
        
int j;
        memset(vis,
0,sizeof(vis));
        
for(j=0;j<p;j++if (!vis[topo[j]]) convisit(topo[j]);
}

void  convisit2( int  i)
{
        
int j;
        vis[i]
=true;
        
for(j=0;j<n;j++){
                
if(g[i][j] && !vis[j]){
                        Union(i,j);
                        convisit(j);
                }

        }

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值